package com.ysnows.base.base

import android.content.Context
import android.os.Bundle
import android.util.Log
import com.trello.rxlifecycle2.components.RxFragment
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity
import com.ysnows.base.config.VConfig
import com.ysnows.base.dialog.QMUIProgressDialog
import com.ysnows.base.emptyview.callback.LoadingCallback
import com.ysnows.base.emptyview.callback.TimeoutCallback
import com.ysnows.base.emptyview.callback.ToLoginCallback
import com.ysnows.base.inter.IRepository
import com.ysnows.base.net.IResp
import com.ysnows.base.net.NetCode
import com.ysnows.base.net.Resp
import com.ysnows.base.utils.Toasts
import com.ysnows.base.utils.UiSwitch
import com.ysnows.base.utils.exception.ExceptionUtils
import com.ysnows.base.view.BView
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit

open class BRepository : IRepository {
    open lateinit var bView: BView

    /**
     * 将View传递进Presenter
     *
     * @param view
     */
    override fun attachView(view: BView) {
        this.bView = view
    }

    override fun init() {}
    override fun view(): BView {
        return bView
    }

    fun <D, T : IResp<D>?> rxLreq(observable: Observable<T>): Observable<T> {
        return rxReq(observable, pageReq = false, loading = true)
    }

    fun <D, T : IResp<D>?> rxPreq(observable: Observable<T>): Observable<T> {
        return rxReq(observable, pageReq = true, loading = false)
    }


    fun <D, T : IResp<D>?> rxReq(observable: Observable<T>): Observable<T> {
        return rxReq(observable, pageReq = false, loading = false)
    }

    /**
     * @param observable
     * @param pageReq 是否是页面请求
     */
    fun <D, T : IResp<D>?> rxReq(observable: Observable<T>, pageReq: Boolean, loading: Boolean): Observable<T> {
        return Observable.create { emitter ->
            val qmuiProgressDialog = arrayOfNulls<QMUIProgressDialog>(1)


            var subscribe = observable
                    .throttleFirst(VConfig.THROTTLE_TIME.toLong(), TimeUnit.MILLISECONDS)
                    .subscribeOn(Schedulers.io())
                    .doOnSubscribe(Consumer { disposable: Disposable? ->
                        disposable?.apply {
                            if (this.isDisposed) {
                                if (pageReq) {
                                    bView.loadService!!.showCallback(LoadingCallback::class.java)
                                }
                                if (loading) {
                                    qmuiProgressDialog[0] = QMUIProgressDialog()
                                    qmuiProgressDialog[0]!!.show(bView.context())
                                }
                            }
                        }
                    } as Consumer<Disposable>)
            if (view() is RxAppCompatActivity) {
                subscribe = subscribe
                        .compose((view() as RxAppCompatActivity?)!!.bindToLifecycle())
            }
            if (view() is RxFragment) {
                subscribe = subscribe
                        .compose((view() as RxFragment?)!!.bindToLifecycle())
            }
            subscribe
                    .subscribeOn(AndroidSchedulers.mainThread())
                    .observeOn(AndroidSchedulers.mainThread())

                    .subscribe(object : Observer<T> {
                        override fun onSubscribe(d: Disposable) {}
                        override fun onNext(iResponse: T) {
                            if (iResponse?.code() == NetCode.CODE_OFFLINE) {
                                if (pageReq) {
                                    bView.loadService?.showCallback(ToLoginCallback::class.java)
                                }
                                bView.toLogin()

                                // bView.processCodeOffline()
                            } else {
                                if (pageReq) {
                                    bView.loadService?.showSuccess()
                                }
                            }
                            emitter.onNext(iResponse)
                        }

                        override fun onError(e: Throwable) {
                            e.printStackTrace()
                            if (pageReq) {
                                bView.loadService!!.showCallback(TimeoutCallback::class.java)
                            } else if (loading) {
                                qmuiProgressDialog[0]!!.dismiss()
                                view().toast(ExceptionUtils.getExceptionName(e))
                            } else {
                                view().toast(ExceptionUtils.getExceptionName(e))
                            }
                            if (view().isNeedRefresh) {
                                view().refreshing(false)
                            }
                            emitter.onError(e)
                        }

                        override fun onComplete() {
                            if (loading) {
                                qmuiProgressDialog[0]!!.dismiss()
                            }
                            if (view().isNeedRefresh) {
                                view().refreshing(false)
                            }
                            emitter.onComplete()
                        }
                    })
        }
    }

    val context: Context?
        get() = view().context()

    override fun <T> single(clazz: Class<T>) {
        UiSwitch.single(view().context(), clazz)
    }

    override fun <T> bundle(clazz: Class<T>, bundle: Bundle?) {
        UiSwitch.bundle(view().context(), clazz, bundle)
    }

    override fun toast(msg: String?) {
        Toasts.toast(view().context(), msg)
    }

    override fun toast(msgId: Int) {
        Toasts.toast(view().context(), msgId)
    }


    suspend fun <T : IResp<*>> lreq(func: suspend () -> T): T {
        return req(loading = true, pageLoading = false, func = func)
    }

    suspend fun <T : IResp<*>> preq(func: suspend () -> T): T {
        return req(loading = false, pageLoading = true, func = func)
    }

    @Suppress("UNCHECKED_CAST")
    suspend fun <T : IResp<*>> req(loading: Boolean = false, pageLoading: Boolean = false, func: suspend () -> T): T {

        var qmuiProgressDialog: QMUIProgressDialog? = null

        try {

            if (pageLoading) {
                bView.loadService?.showCallback(LoadingCallback::class.java)
            }

            if (loading) {
                qmuiProgressDialog = QMUIProgressDialog()
                qmuiProgressDialog.show(bView.context())
            }

            val resp = func()

            if (resp.code() == NetCode.CODE_OFFLINE) {
                when {
                    pageLoading -> {
                        bView.loadService?.showCallback(ToLoginCallback::class.java)
                    }
                    loading -> {
                        qmuiProgressDialog?.dismiss()
                    }
                    view().isNeedRefresh -> {
                        view().refreshing(false)
                    }
                }

                bView.processCodeOffline()
            } else {
                when {
                    pageLoading -> {
                        bView.loadService?.showSuccess()
                    }
                    loading -> {
                        qmuiProgressDialog?.dismiss()
                    }
                    view().isNeedRefresh -> {
                        view().refreshing(false)
                    }
                }
            }
            return resp
        } catch (e: Exception) {

            Log.e("-shy-", "e=: $e")
            e.printStackTrace()
            when {
                pageLoading -> {
                    bView.loadService?.showCallback(TimeoutCallback::class.java)
                }
                loading -> {
                    qmuiProgressDialog?.dismiss()
                    view().toast(ExceptionUtils.getExceptionName(e))
                }
                view().isNeedRefresh -> {
                    view().refreshing(false)
                }
                else -> {
                    view().toast(ExceptionUtils.getExceptionName(e))
                }
            }

            return Resp<Any>(NetCode.CODE_EXCEPTION, msg = ExceptionUtils.getExceptionName(e)) as T
        }
    }


}
