package com.atmob.voiceai.module.subscription;

import android.os.Bundle;
import android.os.SystemClock;

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

import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingResult;
import com.android.billingclient.api.Purchase;
import com.atmob.app.lib.base.BaseViewModel;
import com.atmob.app.lib.handler.RxHttpHandler;
import com.atmob.app.lib.livedata.SingleLiveEvent;
import com.atmob.common.data.KVUtils;
import com.atmob.common.logging.AtmobLog;
import com.atmob.common.runtime.ActivityUtil;
import com.atmob.common.runtime.ContextUtil;
import com.atmob.voiceai.R;
import com.atmob.voiceai.data.api.bean.GoodsBean;
import com.atmob.voiceai.data.api.bean.PayOptionsBean;
import com.atmob.voiceai.data.api.response.OrderPayResponse;
import com.atmob.voiceai.data.consts.ErrorCode;
import com.atmob.voiceai.data.consts.EventId;
import com.atmob.voiceai.data.repositories.MemberRepository;
import com.atmob.voiceai.handlers.EventHandler;
import com.atmob.voiceai.helper.ErrorHelper;
import com.atmob.voiceai.sdk.billing.GPBillingClient;
import com.atmob.voiceai.utils.BoxingUtil;
import com.atmob.voiceai.utils.SpannableUtil;
import com.atmob.voiceai.utils.ToastUtil;

import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import atmob.reactivex.rxjava3.annotations.NonNull;
import atmob.reactivex.rxjava3.core.Observable;
import atmob.reactivex.rxjava3.core.ObservableSource;
import atmob.reactivex.rxjava3.core.SingleObserver;
import atmob.reactivex.rxjava3.core.SingleSource;
import atmob.reactivex.rxjava3.disposables.Disposable;
import atmob.reactivex.rxjava3.functions.Function;
import atmob.reactivex.rxjava3.schedulers.Schedulers;
import atmob.rxjava.utils.RxJavaUtil;
import dagger.hilt.android.lifecycle.HiltViewModel;
import kotlin.Pair;

@HiltViewModel
public class SubscriptionPageViewModel extends BaseViewModel {


    private final String TAG = SubscriptionPageViewModel.class.getSimpleName();
    private final String LAST_BACK_SHOW_RETAIN_TIME = "last_back_show_retain_time";
    private final MutableLiveData<Boolean> showQuerySubLoading = new MutableLiveData<>();

    private final SingleLiveEvent<?> showRetentionDialogEvent = new SingleLiveEvent<>();
    private final SingleLiveEvent<?> finishEvent = new SingleLiveEvent<>();

    private final MutableLiveData<Boolean> isShowCloseBtn = new MutableLiveData<>();
    private final MutableLiveData<List<GoodsBean>> goodsList = new MutableLiveData<>();
    private final GPBillingClient gpBillingClient;
    private List<PayOptionsBean> payList;
    private final MutableLiveData<CharSequence> autoRenewableText = new MutableLiveData<>();
    private final MutableLiveData<CharSequence> subCancelTxt = new MutableLiveData<>();
    private final SingleLiveEvent<Pair<Boolean, String>> showLoading = new SingleLiveEvent<>();
    private final MemberRepository memberRepository;
    private GoodsBean checkGoodsBean;
    private boolean isRequestSubmitOrder;


    @Inject
    public SubscriptionPageViewModel(MemberRepository memberRepository, GPBillingClient gpBillingClient) {
        this.memberRepository = memberRepository;
        this.gpBillingClient = gpBillingClient;
        init();
        refreshSubscriptionDetail();
        gpBillingClient.startUrgentConnection();
        EventHandler.report(EventId.purchase_001);
    }

    public LiveData<Boolean> getShowQuerySubLoading() {
        return showQuerySubLoading;
    }

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

    public void setCheckGoodsBean(GoodsBean checkGoodsBean) {
        this.checkGoodsBean = checkGoodsBean;
    }

    public LiveData<?> getShowRetentionDialogEvent() {
        return showRetentionDialogEvent;
    }

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

    public LiveData<Boolean> getIsShowCloseBtn() {
        return isShowCloseBtn;
    }

    public LiveData<List<GoodsBean>> getGoodsList() {
        return goodsList;
    }

    private void init() {
        initSubTxt();
        addDisposable(RxJavaUtil.timer(3000, TimeUnit.MILLISECONDS, () -> {
            isShowCloseBtn.setValue(true);
        }));
    }

    private void initSubTxt() {
        String allRenewableTxt = "Subscription is auto-renewable.";
        String targetRenewableTxt = "auto-renewable";
        autoRenewableText.setValue(SpannableUtil.getSpannableStringBuilder(allRenewableTxt, targetRenewableTxt, ContextUtil.getContext().getResources().getColor(R.color.colorPrimaryVariant), false));
        String allCancelTxt = "You can cancel your subscription at any time.";
        String targetCancelTxt = "at any time";
        subCancelTxt.setValue(SpannableUtil.getSpannableStringBuilder(allCancelTxt, targetCancelTxt, ContextUtil.getContext().getResources().getColor(R.color.colorPrimaryVariant), false));
    }

    public LiveData<CharSequence> getAutoRenewableText() {
        return autoRenewableText;
    }

    public LiveData<CharSequence> getSubCancelTxt() {
        return subCancelTxt;
    }

    public void onBackClick() {
        EventHandler.report(EventId.purchaseclose_001);
        if (isShowRetentionDialog()) {
            showRetentionDialogEvent.call();
        } else {
            finishEvent.call();
        }
    }

    public void onRestoreClick() {
        EventHandler.report(EventId.restoreclick_001);
        memberRepository.subscriptionResume()
                .subscribe(new SingleObserver<Object>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showLoading.setValue(new Pair<>(true, ContextUtil.getContext().getString(R.string.query_order_status_loading_txt)));
                    }

                    @Override
                    public void onSuccess(@NonNull Object object) {
                        EventHandler.report(EventId.restoresuccess_001);
                        showLoading.setValue(new Pair<>(false, null));
                        finishEvent.call();
                        ToastUtil.show(R.string.member_restore_success, ToastUtil.LENGTH_SHORT);
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoading.setValue(new Pair<>(false, null));
                        if (e instanceof RxHttpHandler.ServerErrorException) {
                            RxHttpHandler.ServerErrorException serverErrorException = (RxHttpHandler.ServerErrorException) e;
                            if (serverErrorException.getCode() != ErrorCode.ERROR_SUBSCRIPTION_NOT_FOUND) {
                                EventHandler.report(EventId.restorefail_001);
                            }
                            ToastUtil.show(serverErrorException.getMsg(), ToastUtil.LENGTH_SHORT);
                        } else {
                            ToastUtil.show(R.string.net_error_message, ToastUtil.LENGTH_SHORT);
                        }
                    }
                });
    }


    private void refreshSubscriptionDetail() {
        memberRepository.payGoodsList()
                .map(payGoodsResponse -> {
                    payList = payGoodsResponse.getPayOptions();
                    return payGoodsResponse.getList();
                })
                .flatMapObservable((Function<List<GoodsBean>, ObservableSource<GoodsBean>>) Observable::fromIterable)
                .flatMapSingle((Function<GoodsBean, SingleSource<GoodsBean>>) memberBean
                        -> gpBillingClient.querySkuDetails(BillingClient.ProductType.SUBS,
                                memberBean.getGoogleProductId(), memberBean.getPlanId(), memberBean.getLowGoogleProductId())
                        .map(gPProductInfo -> {
                            memberBean.setGpProductInfo(gPProductInfo);
                            return memberBean;
                        })
                        .subscribeOn(Schedulers.io())
                        .onErrorReturn(throwable -> memberBean)
                )
                .filter(goodsBean -> goodsBean.getGpProductInfo() != null)
                .toSortedList()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new SingleObserver<List<GoodsBean>>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        showQuerySubLoading.setValue(true);
                    }

                    @Override
                    public void onSuccess(@NonNull List<GoodsBean> goodsBeanList) {
                        goodsList.setValue(goodsBeanList);
                        showQuerySubLoading.setValue(false);
                        if (gpBillingClient.isSupportProductDetails()) {
                            EventHandler.report(EventId.playsethigh_001);
                        } else {
                            EventHandler.report(EventId.playsetlow_001);
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showQuerySubLoading.setValue(false);
                        ErrorHelper.errorThrowableToast(e, ToastUtil.LENGTH_SHORT);
                    }
                });
    }

    public void setRetentionTime() {
        KVUtils.getDefault().putLong(LAST_BACK_SHOW_RETAIN_TIME, SystemClock.elapsedRealtime());
    }

    public boolean isShowRetentionDialog() {
        long lastShowTime = KVUtils.getDefault().getLong(LAST_BACK_SHOW_RETAIN_TIME, 0);
        return SystemClock.elapsedRealtime() - lastShowTime > 12 * 60 * 60 * 1000;
    }


    public void onSubNowClick() {
        if (isRequestSubmitOrder) {
            return;
        }
        if (checkGoodsBean == null) {
            ToastUtil.show(R.string.please_select_goods, ToastUtil.LENGTH_SHORT);
            return;
        }
        if (payList == null || payList.isEmpty()) {
            ToastUtil.show(R.string.no_pay_way, ToastUtil.LENGTH_SHORT);
            return;
        }
        PayOptionsBean payOptionsBean = payList.get(0);
        int payPlatform = payOptionsBean.getPayPlatform();
        int payMethod = payOptionsBean.getPayMethod();
        Bundle bundle = new Bundle();
        bundle.putString(EventId.ID, String.valueOf(checkGoodsBean.getId()));
        EventHandler.report(EventId.purchasesubclick_001, bundle);
        memberRepository.requestGoodsPayOrder(checkGoodsBean.getId(), payPlatform, payMethod)
                .subscribe(new SingleObserver<OrderPayResponse>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        isRequestSubmitOrder = true;
                        showLoading.setValue(new Pair<>(true, null));
                    }

                    @Override
                    public void onSuccess(@NonNull OrderPayResponse orderPayResponse) {
                        isRequestSubmitOrder = false;
                        showLoading.setValue(new Pair<>(false, null));
                        requestPayment(checkGoodsBean, orderPayResponse.getOutTradeNo());
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        isRequestSubmitOrder = false;
                        showLoading.setValue(new Pair<>(false, null));
                        ErrorHelper.errorThrowableToast(e, ToastUtil.LENGTH_SHORT);
                    }
                });
    }

    private void requestPayment(GoodsBean bean, String orderNo) {
        gpBillingClient.subscriptionGoods(ActivityUtil.getTopActivity(),
                        bean.getGpProductInfo().getProductDetails(), bean.getPlanId(), orderNo)
                .compose(RxJavaUtil.SingleSchedule.subThread2Main())
                .subscribe(new SingleObserver<BillingResult>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                    }

                    @Override
                    public void onSuccess(@NonNull BillingResult billingResult) {
                        AtmobLog.d(TAG, "requestPayment onSuccess: " + billingResult);
                        gpBillingClient.registerFlowListener(this, orderNo, purchase -> queryOrderStatus(bean, orderNo, purchase));
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }
                });
    }

    private void queryOrderStatus(GoodsBean bean, String orderNo, Purchase purchase) {
        memberRepository.orderStatus(orderNo, purchase.getPurchaseToken())
                .subscribe(new SingleObserver<Boolean>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable d) {
                        addDisposable(d);
                        addDisposable(RxJavaUtil.timer(25, TimeUnit.SECONDS, () -> {
                            showLoading.setValue(new Pair<>(false, null));
                            ToastUtil.show(R.string.query_order_time_out, ToastUtil.LENGTH_SHORT);
                        }));
                        showLoading.setValue(new Pair<>(true, ContextUtil.getContext().getString(R.string.query_order_status_loading_txt)));
                    }

                    @Override
                    public void onSuccess(@NonNull Boolean aBoolean) {
                        showLoading.setValue(new Pair<>(false, null));
                        if (BoxingUtil.boxing(aBoolean)) {
                            ToastUtil.show(R.string.pay_success, ToastUtil.LENGTH_SHORT);
                            finishEvent.call();
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        showLoading.setValue(new Pair<>(false, null));
                    }
                });
    }

    public void onSubRetentionClick() {
        onSubNowClick();
    }


    @Override
    protected void onCleared() {
        super.onCleared();
        gpBillingClient.endUrgentConnection();
        gpBillingClient.unregisterAllFlowListener(this);
    }
}
