package cn.muchinfo.rma.business.account

import cn.muchinfo.rma.business.account.adapter.AccountAdapter
import cn.muchinfo.rma.global.GlobalDataCollection
import cn.muchinfo.rma.global.data.account.LoginQueryData
import cn.muchinfo.rma.netManage.base.ResponseCallback
import cn.muchinfo.rma.netManage.utils.MyOkHttpUtils
import cn.muchinfo.rma.netcore.packet.Packet50
import cn.muchinfo.rma.netcore.socket.Callback
import cn.muchinfo.rma.netcore.socket.MTP2Socket.ReconnectChangeState_LoginFail
import cn.muchinfo.rma.netcore.socket.MTP2Socket.ReconnectChangeState_Logined
import cn.muchinfo.rma.protobuf.funcode.FunCode
import cn.muchinfo.rma.protobuf.protoclasses.SystemMI1
import cn.muchinfo.rma.view.MyApplication
import cn.muchinfo.rma.view.autoWidget.guard
import cn.muchinfo.rma.view.base.app.BaseResult
import cn.muchinfo.rma.view.base.app.Constant
import cn.muchinfo.rma.view.base.app.ServiceFunApi
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.SPUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.Call
import java.lang.Exception
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.fixedRateTimer

/**
 * 账户管理类
 */
class AccountManager {

    /** 自动Token校验定时器 */
    private var tokenCheckTimer: Timer? = null

    /** 自动Token校验定时器时长 */
    private val tokenCheckInterval: Long = 1000 * 60 * 10

    /**
     * 获取登录ID
     * @param code String 登录代码
     * @param callback Function3<[@kotlin.ParameterName] Boolean, [@kotlin.ParameterName] Error?, [@kotlin.ParameterName] String?, Unit>
     */
    fun queryLoginID(
        code: String,
        callback: (isCompleted: Boolean, err: Error?, loginID: String?) -> Unit
    ) {
        val params = mutableMapOf<String, String>()
        params["username"] = code
        MyOkHttpUtils().queryGetForUrl(
            URL = SPUtils.getInstance().getString(Constant.goCommonSearchUrl) + "/User/GetLoginID",
            params = params,
            callback = object : ResponseCallback<BaseResult<String>>() {
                override fun onError(call: Call?, e: Exception?, id: Int) {
                    callback(false, Error(e?.message), null)
                }

                override fun onResponse(response: BaseResult<String>?, id: Int) {
                    callback(true, null, response?.data)
                }
            }
        )
    }

    /**
     * 账户登录
     * @param IP String 交易接入地址
     * @param port String 交易接入端口
     * @param loginID String 账户名称
     * @param password String 账户密码
     * @param callback Function2<[@kotlin.ParameterName] Boolean, [@kotlin.ParameterName] Error?, Unit>
     */
    fun login(
        IP: String,
        port: String,
        loginID: String,
        password: String,
        callback: (isCompleted: Boolean, err: Error?) -> Unit
    ) {

        val app = MyApplication.getInstance().guard {
            callback(false, Error("Application未初始化"))
            return
        }
        val tradeSocketManager = app.tradeSocketManager.guard {
            callback(false, Error("交易链路未初始化"))
            return
        }

        // 连接交易链路
        tradeSocketManager.conn(IP, port, object : Callback<Packet50> {
            override fun onSuccess(rsp: Packet50?) {
                val reqPacket = AccountAdapter.buildLoginReqPacket(loginID, password)
                // 发送请求
                tradeSocketManager.send(
                    reqPacket,
                    FunCode.LoginRsp,
                    object : Callback<Packet50> {
                        override fun onSuccess(rsp: Packet50?) {
                            val rst = AccountAdapter.analysisLoginRsp(rsp!!)
                            if (rst.first) {
                                // 登录成功
                                thenLogined(rst.third!!)
                            }
                            callback(rst.first, rst.second)
                        }

                        override fun onFail(err: java.lang.Error?) {
                            // 发送数据失败
                            callback(false, err)
                        }
                    })
            }

            override fun onFail(err: java.lang.Error?) {
                callback(false, Error("连接交易服务失败"))
            }
        })
    }

    /**
     * 登录后的业务处理方法，正常登录和三方登录都需要调用此方法
     * @param loginRsp LoginRsp loginRsp
     */
    fun thenLogined(loginRsp: SystemMI1.LoginRsp) {
        val app = MyApplication.getInstance().guard { return }
        val tradeSocketManager = app.tradeSocketManager.guard { return }

        // 记录服务端时间差值，由于服务端在登录后回传的系统时间已经加了时区，所以我们在转成Date对象之前要先把它减去8小时
        GlobalDataCollection.instance?.timeStamp =
            Date(loginRsp.systemTime - 60 * 60 * 8).time - Date().time

        // 记录相关登录信息
        GlobalDataCollection.instance?.loginRsp = loginRsp

        // 成功后通知网络开始发送心跳
        tradeSocketManager.startBeatTime()

        // 通知网络可进行断网重连
        tradeSocketManager.canReconnect = true

        // 开始自动校验Token的方法
        startTokenCheckTimer()
    }

    /**
     * Token校验
     */
    private fun tokenCheck(callback: (isCompleted: Boolean, err: Error?) -> Unit) {
        val app = MyApplication.getInstance().guard {
            return
        }
        val tradeSocketManager = app.tradeSocketManager.guard {
            return
        }

        // 获取LoginID和Token
        val loginRsp = GlobalDataCollection.instance?.loginRsp
        val reqPacket = AccountAdapter.buildTokenCheckReqPacket(loginRsp?.loginID ?: 0, loginRsp?.token ?: "")
        // 发送请求
        tradeSocketManager.send(
            reqPacket,
            FunCode.TokenCheckRsp,
            object : Callback<Packet50> {
                override fun onSuccess(rsp: Packet50?) {
                    val rst = AccountAdapter.analysisTokenCheckRsp(rsp!!)
                    callback(rst.first, rst.second)
                }

                override fun onFail(err: java.lang.Error?) {
                    // 发送数据失败
                    callback(false, err)
                }
            })
    }

    /**
     * 断网重连专用Token检验
     */
    fun tokenCheckAndLoginByReconnected() {
        // 1-5秒后进行Token检验
        val randoms = (1..5).random()
        Observable.timer(randoms.toLong(), TimeUnit.SECONDS)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe {
                tokenCheck() { isCompleted, err ->
                    if (null != err)
                        LogUtils.eTag("RMA", "断网重连专用Token检验失败")

                    MyApplication.getInstance()?.tradeSocketListener?.onReconnectChangeState(
                        MyApplication.getInstance()?.tradeSocketManager,
                        if (isCompleted) ReconnectChangeState_Logined else ReconnectChangeState_LoginFail
                    )
                }
            }
            .let { _ -> }
    }

    /**
     * 开始Token校验定时器
     */
    fun startTokenCheckTimer() {
        stopTokenCheckTimer()

        GlobalScope.launch {
            tokenCheckTimer = fixedRateTimer(null, false, 5000, tokenCheckInterval) {
                tokenCheck() { isCompleted, err ->
                    if (!isCompleted) {
                        // Token检验失败，通知上层返回登录界面（或回到游客状态）
                        MyApplication.getInstance()?.exitApp()
                    }
                }
            }
        }
    }

    /**
     * 停止Token校验定时器
     */
    fun stopTokenCheckTimer() {
        tokenCheckTimer?.cancel()
        tokenCheckTimer = null
    }

    /**
     * 登录后的查询
     * 1、loginAccount 登录账号
     * 2、userAccount
     * 3、userInfo 用户信息
     * 4、markets 暂时无用
     * 5、goodsgroups 商品
     * 6、externalExchanges 外部交易所
     */
    fun loginQuery(callBack: (isCompleted: Boolean, err: Error?) -> Unit) {

        var loginInfo: SystemMI1.LoginRsp = GlobalDataCollection.instance?.loginRsp!!
        val params = mutableMapOf<String, String>()
        params["loginID"] = loginInfo.loginID.toString()
        MyOkHttpUtils().queryGetForUrl(
            URL = SPUtils.getInstance()
                .getString(Constant.goCommonSearchUrl) + ServiceFunApi.LOGIN_QUERY,
            params = params,
            callback = object : ResponseCallback<BaseResult<LoginQueryData>>() {
                override fun onError(call: Call?, e: Exception?, id: Int) {
                    callBack(false, Error(e?.message))
                }

                override fun onResponse(response: BaseResult<LoginQueryData>?, id: Int) {
                    GlobalDataCollection.instance?.loginQueryData = response?.data
                    val goodsUpdateTime = GlobalDataCollection.instance?.goodsInfoUpdateTime
                    val localGoodsUpdataTime =
                        SPUtils.getInstance().getString(Constant.GOODS_UPDATE_TIME)
                    if (localGoodsUpdataTime != goodsUpdateTime) {
                        SPUtils.getInstance().put(Constant.GOODS_UPDATE_TIME, goodsUpdateTime)
                    }
                    callBack(true, null)
                }
            }
        )
    }

}