package com.gh.gamecenter.common.baselist;

import android.annotation.SuppressLint;
import android.app.Application;
import android.app.DownloadManager;

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

import com.therouter.TheRouter;
import com.gh.gamecenter.common.constant.RouteConsts;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.core.provider.IHandleGameResponseProvider;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import retrofit2.HttpException;

/**
 * Created by khy on 2/12/17.
 */

public abstract class ListViewModel<LD /*ListData*/, ID /*ItemData*/> extends BaseListViewModel<ID> implements OnDataObservable<LD> {

    public final static int REQUEST_FAILURE_SIZE = -100;

    protected final MutableLiveData<List<LD>> mListLiveData = new MutableLiveData<>(); // 接口请求返回的数据

    protected final MutableLiveData<HttpException> mLoadExceptionLiveData = new MutableLiveData<>();

    protected LoadParams mRetryParams;
    protected LoadParams mCurLoadParams;

    protected int mOverLimitSize;

    public ListViewModel(@NonNull Application application) {
        super(application);
        mergeResultLiveData();
        initLoadParams();
        mOverLimitSize = mCurLoadParams.getLoadSize() / 2;
    }

    @SuppressLint("CheckResult")
    protected void loadData() {
        if (mCurLoadParams == null) initLoadParams();

        LoadParams loadParams = mRetryParams != null ? mRetryParams : mCurLoadParams;
        Observable<List<LD>> listObservable = provideDataObservable(loadParams.getLoadOffset());
        Single<List<LD>> listSingle = provideDataSingle(loadParams.getLoadOffset());
        LoadStatus curStatus = mLoadStatusLiveData.getValue();

        if ((listObservable == null && listSingle == null) || curStatus != null &&
                curStatus != LoadStatus.INIT_LOADED &&
                curStatus != LoadStatus.LIST_LOADED &&
                curStatus != LoadStatus.INIT) return;

        if (mCurLoadParams.getLoadOffset() == LoadParams.DEFAULT_OFFSET) {
            mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADING);
        } else {
            mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADING);
        }

        if (listObservable != null) {
            listObservable
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .map(this::filterData)
                    .subscribe(new Response<List<LD>>() {
                        @Override
                        public void onResponse(List<LD> response) {
                            handleSuccess(response, curStatus);
                        }

                        @Override
                        public void onFailure(HttpException exception) {
                            handleFailure(exception);
                        }
                    });
        } else if (listSingle != null) {
            listSingle.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .map(this::filterData)
                    .subscribe(new BiResponse<List<LD>>() {
                        @Override
                        public void onSuccess(List<LD> response) {
                            handleSuccess(response, curStatus);
                        }

                        @Override
                        public void onFailure(@NotNull Exception exception) {
                            handleFailure(exception);
                        }
                    });
        }
    }

    private void handleSuccess(List<LD> response, LoadStatus curStatus) {
        List<LD> previousData = mListLiveData.getValue();
        if (previousData == null || mCurLoadParams.getLoadOffset() == LoadParams.DEFAULT_OFFSET ||
                curStatus == LoadStatus.INIT) {
            previousData = new ArrayList<>();
        }

        // 针对游戏的一些操作(过滤隐藏游戏，过滤隐藏APK吗，增加下载数据)
        IHandleGameResponseProvider handleGameResponse = TheRouter.get(IHandleGameResponseProvider.class);
        if (handleGameResponse != null) {
            response = (List<LD>) handleGameResponse.handleGameResponse(response, mEntrance);
        }

        previousData.addAll(response);
        mListLiveData.postValue(previousData);
        loadStatusControl(response.size());
    }

    public MutableLiveData<List<LD>> getListLiveData() {
        return mListLiveData;
    }

    private void handleFailure(Exception exception) {
        if (exception instanceof HttpException && ((HttpException) exception).code() == 404) {
            loadStatusControl(0);
            mLoadStatusLiveData.setValue(LoadStatus.INIT_EXCEPTION);
            mLoadExceptionLiveData.postValue((HttpException) exception);
        } else {
            loadStatusControl(REQUEST_FAILURE_SIZE);
            if (exception instanceof HttpException) {
                mLoadExceptionLiveData.postValue((HttpException) exception);
            }
        }
    }

    protected void loadStatusControl(int size) {
        if (mCurLoadParams.getLoadOffset() == LoadParams.DEFAULT_OFFSET) { // 初始化列表
            if (size == 0) {
                mLoadStatusLiveData.setValue(LoadStatus.INIT_EMPTY);
            } else if (size == REQUEST_FAILURE_SIZE) {
                mLoadStatusLiveData.setValue(LoadStatus.INIT_FAILED);
            } else if (size < mOverLimitSize) { // 避免一个屏幕出现两次分页
                mLoadStatusLiveData.setValue(LoadStatus.INIT_OVER);
            } else {
                mLoadStatusLiveData.setValue(LoadStatus.INIT_LOADED);
            }
        } else {
            if (size == REQUEST_FAILURE_SIZE) {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_FAILED);
            } else if (size == 0) {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_OVER);
            } else {
                mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED);
            }
        }

        if (size == REQUEST_FAILURE_SIZE) {
            mRetryParams = mCurLoadParams;
        } else {
            mRetryParams = null;
            mCurLoadParams.setLoadOffset(mCurLoadParams.getLoadOffset() + 1); // 页数 + 1
        }
    }

    protected void initLoadParams() {
        mCurLoadParams = new LoadParams(LoadParams.DEFAULT_OFFSET);
        mLoadStatusLiveData.setValue(LoadStatus.INIT);
    }

    public void setOverLimitSize(int overLimitSize) {
        mOverLimitSize = overLimitSize;
    }

    @Override
    public void load(LoadType loadType) {
        if (loadType == LoadType.REFRESH) {
            initLoadParams();
        } else if (loadType == LoadType.RETRY) {
            mLoadStatusLiveData.setValue(LoadStatus.LIST_LOADED);
        }
        loadData();
    }

    @Override
    public Single<List<LD>> provideDataSingle(int page) {
        return null;
    }

    protected abstract void mergeResultLiveData();

    public MutableLiveData<HttpException> getLoadExceptionLiveData() {
        return mLoadExceptionLiveData;
    }

    protected List<LD> filterData(@NonNull List<LD> list) {
        return list;
    }
}
