package com.gamedog.vididin.manager

import android.app.Activity
import android.os.Handler
import android.os.Looper
import com.ama.core.architecture.util.AndroidUtil
import com.ama.core.architecture.util.DateUtil
import com.ama.core.architecture.util.DeviceUtil
import com.ama.core.architecture.util.MD5Util
import com.ama.core.architecture.util.SpUtil
import com.ama.core.architecture.util.eventbus.NotifyMan
import com.gamedog.vididin.VidiConst
import com.gamedog.vididin.VidiConst.ZEROBUY_SECRET
import com.gamedog.vididin.VididinEvents
import com.gamedog.vididin.beans.RECORD_CASH_ZERO_WITHDRAW_SUCCESS
import com.gamedog.vididin.beans.RecordCash
import com.gamedog.vididin.beans.ZeroBuyItem
import com.gamedog.vididin.beans.ZeroBuyWithdrawResp
import com.gamedog.vididin.core.login.login.AccountManager
import com.gamedog.vididin.features.withdraw.dialogs.WithdrawBindBankDialog
import com.gamedog.vididin.features.withdraw.dialogs.WithdrawInfoConfirmDialog
import com.gamedog.vididin.manager.WithdrawManager.Companion.TRANSACTION_STATE_FAIL
import com.gamedog.vididin.manager.WithdrawManager.Companion.TRANSACTION_STATE_ONGOING
import com.gamedog.vididin.manager.WithdrawManager.Companion.TRANSACTION_STATE_SUCCESS
import com.gamedog.vididin.manager.WithdrawManager.Companion.TRANSACTION_STATE_UNSTART
import com.gamedog.vididin.netbase.NetworkUtil
import com.gamedog.vididin.netbase.Result
import com.gamedog.vididin.request.RequestUtil
import com.viddin.videos.free.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.util.concurrent.locks.ReentrantLock
import kotlin.Int


class ZeroManager private constructor() {
    companion object {
        @Volatile
        private var instance: ZeroManager? = null

        fun instance(): ZeroManager {
            return instance ?: synchronized(this) {
                instance ?: ZeroManager().also {
                    instance = it
                }
            }
        }
    }

    private val mBgScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
    private val mRecordList: MutableList<WinZeroWithdrawInfoItem> by lazy {
        SpUtil.instance().getList<WinZeroWithdrawInfoItem>(SpUtil.KEY_ZEROBUY_WIN_ITEMS).toMutableList()
    }

    private val mRecordLocker = ReentrantLock()
    private val mLooperHandler = Handler(Looper.getMainLooper())
    private val mLoopRunnable = object : Runnable {
        override fun run() {
            loopCheckTransactionState()
        }
    }

    init {
        loopCheckTransactionState()
        mLooperHandler.postDelayed(mLoopRunnable, 20 * 1000)
    }

    private fun saveWinWithdrawInfos() {
        SpUtil.instance().putList(SpUtil.KEY_ZEROBUY_WIN_ITEMS, mRecordList)
        notifyWithdrawListChanged()
    }

    private fun notifyWithdrawListChanged() {
        NotifyMan.instance().sendEvent(VididinEvents.EVENT_ZERO_WITHDRAW_LIST_CHANGED, null)
    }


    fun getWaitNotifyResultList() : List<WinZeroWithdrawInfoItem> {
        return mRecordList.filter { it.withdrawState == TRANSACTION_STATE_SUCCESS && !it.hasShowResultDialog }
    }

    /**
     * only add new one, if already exist - then do nothing
     */
    fun addWinWithdrawItem(zeroItem: ZeroBuyItem) {
        val userId = AccountManager.getAccount().userId
        zeroItem.winners?.let {
            if (it.contains(userId) && zeroItem.completed) {
                var alreadyExist = false

                try {
                    mRecordLocker.lock()
                    mRecordList.forEach {
                        if (it.purchase_id == zeroItem.id) {
                            alreadyExist = true
                            return@forEach
                        }
                    }

                    if (!alreadyExist) {
                        mRecordList.add(WinZeroWithdrawInfoItem(zeroItem.id, zeroItem.title, zeroItem.price!!))
                        saveWinWithdrawInfos()
                    }
                } finally {
                    mRecordLocker.unlock()
                }
            }
        }
    }

    fun getZeroWithdrawItem(zeroItem: ZeroBuyItem): WinZeroWithdrawInfoItem {
        try {
            mRecordLocker.lock()
            return mRecordList.filter { zeroItem.id == it.purchase_id }[0]
        } finally {
            mRecordLocker.unlock()
        }
    }

    fun couldStartWithdraw(zeroItem: ZeroBuyItem): Boolean {
        val userId = AccountManager.getAccount().userId
        zeroItem.winners?.let {
            if (it.contains(userId) && zeroItem.completed) {
                try {
                    mRecordLocker.lock()
                    mRecordList.forEach {
                        if (it.purchase_id == zeroItem.id) {
                            return it.withdrawState == TRANSACTION_STATE_UNSTART || it.withdrawState == TRANSACTION_STATE_FAIL
                        }
                    }
                    addWinWithdrawItem(zeroItem)
                    return true
                } finally {
                    mRecordLocker.unlock()
                }
            }
        }

        return false
    }



    private fun requestZeroWithdrawReward(zeroWithdrawItem: WinZeroWithdrawInfoItem) {
        mBgScope.launch {
            // header
            val operationVal = VidiConst.ZERO_WITHDRAW_OPERATION
            val curTimeSec = System.currentTimeMillis()/1000
            val signStr = RequestUtil.getZeroBuyRequestSign(curTimeSec, operationVal)
            val requestHeaders = mapOf("Operation" to operationVal.toString(), "Timestamp" to curTimeSec.toString(), "Sign" to signStr)

            // body param
            val requestParams: MutableMap<String, String> = mutableMapOf("AppId" to AndroidUtil.getPackageId(), "DeviceId" to DeviceUtil.generateDeviceId())
            val userId = AccountManager.getAccount().userId
            if (userId > 0) {
                requestParams.put("UserId", userId.toString())
            }
            val joinZeroBuyItemIds = SpUtil.instance().getList<Int>(SpUtil.KEY_ZEROBUY_JOINED_ACTIVITY_IDS)
            requestParams.put("ActivityId", zeroWithdrawItem.purchase_id.toString())

            // withdraw 相关参数：
            val bankCPFAccount = AccountManager.getAccount().bankInfo?.bankAccount!!
            val accountType = "CPF"
            requestParams.put("Account", bankCPFAccount)
            requestParams.put("AccountType", accountType)
            requestParams.put("Country", Country.BR.name)
            requestParams.put("DocumentType", accountType)
            requestParams.put("DocumentId", bankCPFAccount)
            requestParams.put("BankCode", AccountManager.getAccount().bankInfo?.bankAccount!!)
            val signOriginStr = "${zeroWithdrawItem.purchase_id}-${userId}-${bankCPFAccount}-${accountType}-${bankCPFAccount}-${accountType}-${ZEROBUY_SECRET}"
            requestParams.put("Sign", MD5Util.md5(signOriginStr)!!)


            val result = NetworkUtil.post("${VidiConst.URL_ZERO_BUY}/any", requestHeaders, requestParams, joinZeroBuyItemIds)
            when (result) {
                is Result.Success -> {
                    val respObj = AndroidUtil.json2Object<ZeroBuyWithdrawResp>(result.data.string())

                    respObj?.let {
                        if (it.code == 0 && it.content.isNotEmpty()) {
                            zeroWithdrawItem.orderId = respObj.content
                            zeroWithdrawItem.withdrawState = TRANSACTION_STATE_ONGOING
                            saveWinWithdrawInfos()
                            loopCheckTransactionState()
                            AndroidUtil.showToast(R.string.zero_withdraw_ongoing)
                            return@launch
                        } else {
                            zeroWithdrawItem.withdrawState = TRANSACTION_STATE_FAIL
                            zeroWithdrawItem.failReason = respObj.code
                            saveWinWithdrawInfos()
                            AndroidUtil.showToast(R.string.zero_withdraw_failed)
                        }
                    }
                }
                is Result.Error -> {
                    AndroidUtil.showToast(R.string.net_error)
                }
                is Result.Loading -> {

                }
            }
        }
    }


    private fun requestZeroWithdrawResult(zeroWithdrawItem: WinZeroWithdrawInfoItem) {
        mBgScope.launch {
            // header
            val operationVal = VidiConst.ZERO_WITHDRAW_RESULT_CHECK
            val curTimeSec = System.currentTimeMillis()/1000
            val signStr = RequestUtil.getZeroBuyRequestSign(curTimeSec, operationVal)
            val requestHeaders = mapOf("Operation" to operationVal.toString(), "Timestamp" to curTimeSec.toString(), "Sign" to signStr)

            // body param
            val requestParams: MutableMap<String, String> = mutableMapOf("AppId" to AndroidUtil.getPackageId())
            val userId = AccountManager.getAccount().userId
            if (userId > 0) {
                requestParams.put("UserId", userId.toString())
            }

            requestParams.put("ActivityId", zeroWithdrawItem.purchase_id.toString())

            // withdraw check result 相关参数：
            requestParams.put("device_id", zeroWithdrawItem.orderId)
            requestParams.put("order_id", zeroWithdrawItem.orderId)
            requestParams.put("record_id", zeroWithdrawItem.orderId)
            requestParams.put("recordId", zeroWithdrawItem.orderId)


            val result = NetworkUtil.post("${VidiConst.URL_ZERO_BUY}/any", requestHeaders, requestParams)
            //val result = NetworkUtil.post("http://192.168.110.141:8081/any", requestHeaders, requestParams)

            when (result) {
                is Result.Success -> {
                    val respObj = AndroidUtil.json2Object<ZeroBuyWithdrawResp>(result.data.string())

                    respObj?.let {
                        if (it.code == 0) {
                            zeroWithdrawItem.withdrawState = TRANSACTION_STATE_SUCCESS
                            zeroWithdrawItem.operateMs = DateUtil.getCurTimeMs()
                            saveWinWithdrawInfos()
                            notifyWithdrawListChanged()
                            saveSuccessWithdraw2CashRecord(zeroWithdrawItem)
                            return@launch
                        }
                    }
                }
                is Result.Error -> {
                    AndroidUtil.showToast(R.string.net_error)
                }
                is Result.Loading -> {

                }
            }
        }
    }

    private fun saveSuccessWithdraw2CashRecord(withdrawItem: WinZeroWithdrawInfoItem) {
        var withdrawCashNum = 0.0
        try {
            if (withdrawItem.winCashNumStr.startsWith("R$") && withdrawItem.winCashNumStr.length > "R$".length) {
                withdrawCashNum = withdrawItem.winCashNumStr.substring("R$".length).toDouble()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        RecordsManager.instance().appendCashRecord(RecordCash(RECORD_CASH_ZERO_WITHDRAW_SUCCESS, withdrawCashNum))
    }

    fun startWithdrawProcess(activity: Activity, item: ZeroBuyItem) {
        val zeroWithdrawInfoItem = getZeroWithdrawItem(item)

        if (couldStartWithdraw(item)) {
            val onConfirmed: (cashNum: Double)->Unit = {
                requestZeroWithdrawReward(zeroWithdrawInfoItem)
            }

            if (AccountManager.isBankAccountExist()) {
                WithdrawInfoConfirmDialog(activity, onConfirmed).setWithDrawCashNumStr(zeroWithdrawInfoItem.winCashNumStr!!).show()
            } else {
                WithdrawBindBankDialog(activity = activity, onConfirmed).setWithDrawCashNumStr(zeroWithdrawInfoItem.winCashNumStr!!).show()
            }
        } else {
            when (zeroWithdrawInfoItem.withdrawState) {
                TRANSACTION_STATE_ONGOING -> {
                    AndroidUtil.showToast(R.string.claim_reward_onging)
                }

                TRANSACTION_STATE_SUCCESS -> {
                    AndroidUtil.showToast(R.string.has_claimed_reward)
                }
            }
        }
    }





    private fun loopCheckTransactionState() {
        var unCheckCount = 0
        try {
            mRecordLocker.lock()
            mRecordList.forEach { record ->
                if (record.withdrawState == TRANSACTION_STATE_ONGOING) {
                    unCheckCount++
                }
            }
        } finally {
            mRecordLocker.unlock()
        }


        if (unCheckCount > 0) {
            try {
                mRecordLocker.lock()
                mRecordList.forEachIndexed { index, record ->
                    if (record.withdrawState == TRANSACTION_STATE_ONGOING) {
                        requestZeroWithdrawResult(record)
                    }
                }
            } finally {
                mRecordLocker.unlock()
            }

            mLooperHandler.postDelayed(mLoopRunnable, 20 * 1000)
        }
    }

    fun updateHasNotifyValue(purchaseId: Int) {
        mRecordList.forEach {
            if (it.purchase_id == purchaseId) {
                it.hasShowResultDialog = true
                saveWinWithdrawInfos()
                return@forEach
            }
        }
    }

    fun getZeroHasWithdrawSuccessCashCount(): Float {
        var withdrawSuccessCount = 0F
        mRecordList.forEach {
            try {
                val withdrawItem = it
                if (withdrawItem.withdrawState == TRANSACTION_STATE_SUCCESS
                    && withdrawItem.winCashNumStr.startsWith("R$")
                    && withdrawItem.winCashNumStr.length > "R$".length) {
                    withdrawSuccessCount += withdrawItem.winCashNumStr.substring("R$".length).toFloat()
                }
            } catch (e: Exception) {

            }
        }
        return withdrawSuccessCount
    }

}



data class WinZeroWithdrawInfoItem(
    val purchase_id: Int = 0,
    val purchase_title: String? = "",
    val winCashNumStr: String = "",
    var operateMs: Long = System.currentTimeMillis(),
    var withdrawState: Int = TRANSACTION_STATE_UNSTART,
    var failReason: Int = 0,
    var hasShowResultDialog: Boolean = false,
    var orderId: String = "",
)



enum class Country(val code: Int) {
    ANY(0),        // 任意国家
    BR(1),         // 巴西
    TR(2),         // 土耳其
    PK(3)          // 巴基斯坦
}

enum class Currency(val code: Int) {
    ANY(0),        // 任意国家货币
    BRL(1),        // 巴西雷亚尔
    TRY(2),        // 土耳其里拉
    PKR(3)         // 巴基斯坦卢比
}

enum class Language(val code: Int) {
    ANY(0),        // 任意国家语言
    PT(1),         // 葡萄牙语（巴西，葡萄牙）
    TR(2),         // 土耳其语（土耳其）
    UR(3)          // 乌尔都语（巴基斯坦）
}