package com.datarecovery.master.data.repositories;

import android.text.TextUtils;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;

import com.atmob.common.data.KVUtils;
import com.atmob.common.logging.AtmobLog;
import com.datarecovery.master.R;
import com.datarecovery.master.data.api.AtmobApi;
import com.datarecovery.master.data.api.request.BaseRequest;
import com.datarecovery.master.data.api.request.LoginRequest;
import com.datarecovery.master.data.api.request.SendCodeRequest;
import com.datarecovery.master.data.api.response.LoginResponse;
import com.datarecovery.master.data.consts.ErrorCode;
import com.datarecovery.master.utils.BoxingUtil;
import com.datarecovery.master.utils.RxHttpHandler;
import com.datarecovery.master.utils.ToastUtil;

import javax.inject.Inject;
import javax.inject.Singleton;

import atmob.reactivex.rxjava3.annotations.NonNull;
import atmob.reactivex.rxjava3.core.Single;
import atmob.reactivex.rxjava3.core.SingleObserver;
import atmob.reactivex.rxjava3.disposables.Disposable;
import atmob.rxjava.utils.RxJavaUtil;

@Singleton
public class AccountRepository {


    private static final String TAG = "AccountRepository";
    private static final String KEY_LOGIN_PHONE_NUM = "key_account_repo_login_phone_num";
    private static final String KEY_LOGIN_TOKEN = "key_account_repo_login_token";
    private final MutableLiveData<String> loginPhoneNum = new MutableLiveData<>();
    private final LiveData<Boolean> isLogin = Transformations.map(loginPhoneNum, phoneNum -> !TextUtils.isEmpty(phoneNum));
    private final MutableLiveData<?> memberStatusInfo = new MutableLiveData<>();
    public static String token;
    private final AtmobApi atmobApi;
    private final DeviceFuncRepository deviceFuncRepository;
    private final PayRepository payRepository;

    private int errorCodeTimes;

    private long lastRequestCodeTime;

    static {
        token = KVUtils.getDefault().getString(KEY_LOGIN_TOKEN, null);
    }

    @Inject
    public AccountRepository(AtmobApi atmobApi, DeviceFuncRepository deviceFuncRepository, PayRepository payRepository) {
        this.atmobApi = atmobApi;
        this.deviceFuncRepository = deviceFuncRepository;
        this.payRepository = payRepository;
        loginPhoneNum.setValue(KVUtils.getDefault().getString(KEY_LOGIN_PHONE_NUM, null));
        isLogin.observeForever(isLogin -> AtmobLog.d(TAG, "isLogin: " + isLogin));
        deviceFuncRepository.refreshFuncAuths();
    }

    public LiveData<String> getLoginPhoneNum() {
        return loginPhoneNum;
    }

    public LiveData<Boolean> getIsLogin() {
        return isLogin;
    }

    private void onLoginSuccess(String phoneNum, String token) {
        AccountRepository.token = token;
        this.loginPhoneNum.setValue(phoneNum);
        deviceFuncRepository.refreshFuncAuths();
        payRepository.refreshOrderPageList();
        KVUtils.getDefault().putString(KEY_LOGIN_PHONE_NUM, phoneNum);
        KVUtils.getDefault().putString(KEY_LOGIN_TOKEN, token);
    }


    public Single<?> requestUserCode(String phoneNum) {
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastRequestCodeTime < 60 * 1000) {
            return Single.error(new RequestCodeTooOftenException());
        }
        return atmobApi.loginSendCode(new SendCodeRequest(phoneNum))
                .compose(RxHttpHandler.handle(true))
                .compose(RxJavaUtil.SingleSchedule.io2Main())
                .doOnSuccess(o -> {
                    lastRequestCodeTime = currentTime;
                    errorCodeTimes = 0;
                });
    }

    public Single<LoginResponse> login(String phoneNum, String verificationCode) {
        if (errorCodeTimes >= 5) {
            return Single.error(new LoginTooOftenException());
        }
        LoginRequest loginRequest = new LoginRequest(phoneNum, verificationCode);
        return atmobApi.loginUserLogin(loginRequest)
                .compose(RxHttpHandler.handle(true))
                .compose(RxJavaUtil.SingleSchedule.io2Main())
                .doOnSuccess(response -> {
                    errorCodeTimes = 0;
                    onLoginSuccess(phoneNum, response.getToken());
                })
                .doOnError(throwable -> {
                    RxHttpHandler.ServerErrorException serverErrorException
                            = throwable instanceof RxHttpHandler.ServerErrorException ? ((RxHttpHandler.ServerErrorException) throwable) : null;
                    if (serverErrorException != null) {
                        if (serverErrorException.getCode() == ErrorCode.ERROR_CODE_VERIFICATION_CODE_ERROR) {
                            errorCodeTimes++;
                        }
                    }
                });
    }

    public void requestUserLogout() {
        userLogout().subscribe(new SingleObserver<Object>() {
            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onSuccess(@NonNull Object o) {
                logout();
            }

            @Override
            public void onError(@NonNull Throwable e) {
                ToastUtil.show(R.string.account_logout_fail, ToastUtil.LENGTH_SHORT);
            }
        });
    }

    public void logout() {
        if (!BoxingUtil.boxing(isLogin.getValue())) {
            return;
        }
        token = null;
        deviceFuncRepository.refreshFuncAuths();
        payRepository.refreshOrderPageList();
        KVUtils.getDefault().putString(KEY_LOGIN_PHONE_NUM, "");
        KVUtils.getDefault().putString(KEY_LOGIN_TOKEN, "");
        if (BoxingUtil.boxing(isLogin.getValue())) {
            loginPhoneNum.postValue(null);
            memberStatusInfo.postValue(null);
        }
    }

    private Single<Object> userLogout() {
        return atmobApi.userLogout(new BaseRequest())
                .compose(RxHttpHandler.handle(true))
                .compose(RxJavaUtil.SingleSchedule.io2Main());
    }


    public static class RequestCodeTooOftenException extends Exception {
    }

    public static class LoginTooOftenException extends Exception {
    }

}
