package com.datarecovery.master.module.login;

import android.accounts.NetworkErrorException;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.atmob.app.lib.base.BaseViewModel;
import com.atmob.app.lib.handler.RxHttpHandler;
import com.atmob.app.lib.livedata.SingleLiveEvent;
import com.datarecovery.master.R;
import com.datarecovery.master.data.consts.ErrorCode;
import com.datarecovery.master.data.consts.EventId;
import com.datarecovery.master.data.repositories.AccountRepository;
import com.datarecovery.master.handler.EventHelper;
import com.datarecovery.master.utils.BoxingUtil;
import com.datarecovery.master.utils.Maps;
import com.datarecovery.master.utils.ToastUtil;

import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;

import atmob.reactivex.rxjava3.core.SingleObserver;
import atmob.reactivex.rxjava3.disposables.Disposable;
import atmob.rxjava.utils.RxJavaUtil;
import dagger.hilt.android.lifecycle.HiltViewModel;


@HiltViewModel
public class LoginViewModel extends BaseViewModel {

    private final SingleLiveEvent<Boolean> showLoading = new SingleLiveEvent<>();
    private final MutableLiveData<String> phoneNum = new MutableLiveData<>();
    private final MutableLiveData<Boolean> isRequestCodeCountdown = new MutableLiveData<>(false);
    private final MutableLiveData<Boolean> isCheckedAgreement = new MutableLiveData<>(false);
    private final MutableLiveData<String> requestCodeCountdown = new MutableLiveData<>();
    private final MutableLiveData<String> verificationCode = new MutableLiveData<>();
    private final MutableLiveData<CharSequence> agreeText = new MutableLiveData<>();
    private final SingleLiveEvent<?> finishEvent = new SingleLiveEvent<>();
    private final AccountRepository accountRepository;
    private Disposable getCodeCountdownDisposable;
    private String reportId;

    @Inject
    public LoginViewModel(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
        init();
    }


    public LiveData<Boolean> getShowLoading() {
        return showLoading;
    }

    public MutableLiveData<String> getPhoneNum() {
        return phoneNum;
    }

    public MutableLiveData<String> getVerificationCode() {
        return verificationCode;
    }

    public MutableLiveData<Boolean> getIsCheckedAgreement() {
        return isCheckedAgreement;
    }

    public LiveData<Boolean> getIsRequestCodeCountdown() {
        return isRequestCodeCountdown;
    }

    public LiveData<?> getFinishEvent() {
        return finishEvent;
    }

    public LiveData<String> getRequestCodeCountdown() {
        return requestCodeCountdown;
    }

    public LiveData<CharSequence> getAgreeText() {
        return agreeText;
    }

    public void onBackClick() {
        finishEvent.call();
    }


    private void init() {

    }


    public void onGetCodeClick() {
        String phoneNumText = phoneNum.getValue();
        if (isPhone(phoneNumText)) {
            doRequestVerificationCode(phoneNumText);
        } else {
            ToastUtil.show(R.string.login_phone_num_11, ToastUtil.LENGTH_SHORT);
        }
    }

    private void doRequestVerificationCode(String phoneNumText) {
        if (BoxingUtil.boxing(isRequestCodeCountdown.getValue())) {
            ToastUtil.show(R.string.login_request_code_frequently_toast, ToastUtil.LENGTH_SHORT);
            return;
        }
        accountRepository.requestUserCode(phoneNumText)
                .subscribe(new SingleObserver<Object>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showLoading.setValue(true);
                    }

                    @Override
                    public void onSuccess(@NonNull Object o) {
                        startGetCodeCountdown();
                        showLoading.setValue(false);
                        ToastUtil.show(R.string.login_verification_code_request_success_toast, ToastUtil.LENGTH_SHORT);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoading.setValue(false);
                        if (e instanceof AccountRepository.RequestCodeTooOftenException) {
                            ToastUtil.show(R.string.login_request_code_frequently_toast, ToastUtil.LENGTH_SHORT);
                            return;
                        }
                        e.printStackTrace();
                        stopGetCodeCountdown();
                        ToastUtil.show(R.string.login_verification_code_request_failed_toast, ToastUtil.LENGTH_SHORT);
                    }
                });
    }

    private void stopGetCodeCountdown() {
        if (getCodeCountdownDisposable != null) {
            getCodeCountdownDisposable.dispose();
        }
        isRequestCodeCountdown.setValue(false);
    }

    private void startGetCodeCountdown() {
        if (getCodeCountdownDisposable != null) {
            getCodeCountdownDisposable.dispose();
        }
        isRequestCodeCountdown.setValue(true);
        getCodeCountdownDisposable = RxJavaUtil.interval(0, 1, 60, TimeUnit.SECONDS,
                index -> requestCodeCountdown.setValue(String.valueOf(60 - index)), () -> isRequestCodeCountdown.setValue(false));

        addDisposable(getCodeCountdownDisposable);
    }

    public void onLoginClick() {
        EventHelper.report(EventId.hf1000302);
        String phoneNumText = phoneNum.getValue();
        if (!isPhone(phoneNumText)) {
            ToastUtil.show(R.string.login_phone_num_11, ToastUtil.LENGTH_SHORT);
            return;
        }
        String code = verificationCode.getValue();
        if (TextUtils.isEmpty(code)) {
            ToastUtil.show(R.string.login_code_hint, ToastUtil.LENGTH_SHORT);
            return;
        }
        if (!BoxingUtil.boxing(isCheckedAgreement.getValue())) {
            EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11019"));
            ToastUtil.show(R.string.login_please_agree, ToastUtil.LENGTH_SHORT);
            return;
        }
        accountRepository.login(phoneNum.getValue(), verificationCode.getValue())
                .subscribe(new SingleObserver<Object>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showLoading.setValue(true);
                    }

                    @Override
                    public void onSuccess(@NonNull Object o) {
                        showLoading.setValue(false);
                        ToastUtil.show(R.string.login_success, ToastUtil.LENGTH_SHORT);
                        finishEvent.call();
                        EventHelper.report(EventId.hf1000303);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoading.setValue(false);
                        if (e instanceof AccountRepository.LoginTooOftenException) {
                            EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11022"));
                            ToastUtil.show(R.string.login_too_often_toast, ToastUtil.LENGTH_SHORT);
                            return;
                        }
                        if (e instanceof NetworkErrorException) {
                            EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11019"));
                            ToastUtil.show(R.string.net_error, ToastUtil.LENGTH_SHORT);
                            return;
                        }
                        RxHttpHandler.ServerErrorException serverErrorException
                                = e instanceof RxHttpHandler.ServerErrorException ? ((RxHttpHandler.ServerErrorException) e) : null;
                        if (serverErrorException != null) {
                            if (serverErrorException.getCode() == ErrorCode.ERROR_CODE_VERIFICATION_CODE_ERROR) {
                                EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11021"));
                                ToastUtil.show(R.string.login_verification_code_error_toast, ToastUtil.LENGTH_SHORT);
                            } else {
                                EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11022"));
                                ToastUtil.show(R.string.login_failed_toast, ToastUtil.LENGTH_SHORT);
                            }
                        } else {
                            EventHelper.report(EventId.hf1000304, Maps.asMap(EventId.EVENT_ID, "hf11022"));
                            ToastUtil.show(R.string.login_failed_toast, ToastUtil.LENGTH_SHORT);
                        }
                    }
                });
    }


    public boolean isCanLogin(String phoneNum, String verificationCode) {
        return isPhone(phoneNum) && verificationCode != null && verificationCode.length() > 0;
    }

    private boolean isPhone(String phoneNumText) {
        Pattern phonePattern = Pattern.compile("^1\\d{10}$");
        if (phoneNumText == null) {
            return false;
        }
        phoneNumText = phoneNumText.trim();
        if (TextUtils.isEmpty(phoneNumText)) {
            return false;
        }
        Matcher matcher = phonePattern.matcher(phoneNumText);
        return matcher.matches();
    }

    public void setReportId(String reportId) {
        if (!TextUtils.isEmpty(this.reportId)) {
            return;
        }
        this.reportId = reportId;
        EventHelper.report(EventId.hf1000301, Maps.asMap(EventId.EVENT_ID, reportId));
    }
}
