package com.adealink.weparty.wallet.pay

import android.app.Activity
import android.content.Intent
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.base.AppBase
import com.adealink.frame.base.Rlt
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.frame.BaseFrame
import com.adealink.frame.frame.IListener
import com.adealink.frame.log.Log
import com.adealink.weparty.App
import com.adealink.weparty.commonui.ext.isFailure
import com.adealink.weparty.module.account.AccountModule
import com.adealink.weparty.module.wallet.data.Currency
import com.adealink.weparty.module.wallet.data.PayChannel
import com.adealink.weparty.module.wallet.data.PayChannelUnKnowError
import com.adealink.weparty.module.wallet.data.PayError
import com.adealink.weparty.module.wallet.data.Purchase
import com.adealink.weparty.module.wallet.data.RechargeListEmptyError
import com.adealink.weparty.module.wallet.data.SkuInfo
import com.adealink.weparty.wallet.R
import com.adealink.weparty.wallet.data.CONSUME_OPEN
import com.adealink.weparty.wallet.data.SOURCE_BUY
import com.adealink.weparty.wallet.data.SOURCE_COMPENSATE
import com.adealink.weparty.wallet.data.TAG_PAY
import com.adealink.weparty.wallet.data.VERIFY_MAX_RETRY_TIME
import com.adealink.weparty.wallet.data.VERIFY_OPEN
import com.adealink.weparty.wallet.datasource.remote.WalletHttpService
import com.adealink.weparty.wallet.manager.walletManager
import com.adealink.weparty.wallet.pay.model.PurchaseResult
import com.adealink.weparty.wallet.recharge.data.PaySuccessReq
import com.adealink.weparty.wallet.recharge.data.PrePurchaseReq
import com.adealink.weparty.wallet.recharge.data.RechargeConfigReq
import com.adealink.weparty.wallet.recharge.data.RechargeProduct
import com.adealink.weparty.wallet.stat.FirstPaymentEvent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

val payManager: IPayManager by lazy { PayManager() }

/**
 *
 */
class PayManager : IPayManager, IPayListener, BaseFrame<IListener>() {

    private val googleBilling by lazy {
        GoogleBilling().apply {
            addListener(this@PayManager)
        }
    }

    private val walletHttpService by lazy {
        App.instance.networkService.getHttpService(WalletHttpService::class.java)
    }

    private var productInfoListCache = mutableMapOf<Currency, List<RechargeProduct>>()

    private fun getPay(channel: PayChannel): IPay {
        return when (channel) {
            PayChannel.Google -> googleBilling
        }
    }

    override suspend fun setupPayChannel(channel: PayChannel): Rlt<Any> {
        return getPay(channel).setup()
    }

    override suspend fun getAllRechargeList(
        channel: PayChannel,
    ): Rlt<Map<Currency, List<RechargeProduct>>> {
        val coinDef = getRechargeList(channel, Currency.COIN)
        val diamondDef = getRechargeList(channel, Currency.DIAMOND)
        return if (coinDef.isFailure) {
            coinDef as Rlt.Failed
        } else if (diamondDef.isFailure) {
            diamondDef as Rlt.Failed
        } else {
            Rlt.Success(productInfoListCache)
        }
    }


    override suspend fun getRechargeList(
        channel: PayChannel,
        currency: Currency
    ): Rlt<List<RechargeProduct>> {
        when (val result =
            walletHttpService.rechargeConfig(RechargeConfigReq(currency = currency.type))
                .apply {
                    Log.logRltD(TAG_PAY, "getRechargeList", this)
                }) {
            is Rlt.Success -> {
                val res = result.data.data?.items
                val productInfoList = res
                if (productInfoList.isNullOrEmpty()) {
                    return Rlt.Failed(RechargeListEmptyError())
                }

                //获取三方套餐列表
                var resultList: List<RechargeProduct>
                //测试包直接返回充值套餐
                if (!AppBase.isRelease) {
                    resultList =
                        getDebugDisplayProductList(productInfoList, channel, currency)
                    return Rlt.Success(resultList)
                }
                val skuListRlt = getPay(channel).querySkuDetails(productInfoList.map { it.skuId() })
                val skuInfoList = mutableListOf<SkuInfo>()
                when (skuListRlt) {
                    is Rlt.Success -> {
                        skuListRlt.data.forEach { skuInfo ->
                            skuInfoList.add(skuInfo)
                        }
                        resultList =
                            getDisplayProductList(productInfoList, skuInfoList, channel, currency)
                    }

                    is Rlt.Failed -> {
                        return skuListRlt
                    }
                }
                productInfoListCache[currency] = resultList
                return Rlt.Success(resultList)
            }

            is Rlt.Failed -> {
                return result
            }
        }
    }

    private fun getDebugDisplayProductList(
        originProductInfo: List<RechargeProduct>,
        channel: PayChannel,
        currency: Currency
    ): List<RechargeProduct> {
        if (AppBase.isRelease) {
            throw IllegalStateException("can not invoke getDebugDisplayProductList in release package!!!")
        }
        val displayProductList = mutableListOf<RechargeProduct>()
        originProductInfo.forEach { productInfo ->
            productInfo.productChannel = channel.type
            productInfo.productCurrency = currency.type
            displayProductList.add(productInfo)
        }
        return displayProductList
    }

    private fun getDisplayProductList(
        originProductInfo: List<RechargeProduct>,
        skuInfoList: List<SkuInfo>?,
        channel: PayChannel,
        currency: Currency
    ): List<RechargeProduct> {
        val displayProductList = mutableListOf<RechargeProduct>()
        originProductInfo.forEach { productInfo ->
            productInfo.productChannel = channel.type
            productInfo.productCurrency = currency.type
            val skuInfo = skuInfoList?.find { it.getSkuId() == productInfo.skuId() }
            if (skuInfo != null) {
                //套餐补充信息
//                productInfo.skuId = skuInfo.getSkuId()
//                productInfo.priceAmountCents = skuInfo.getPriceAmountMicros() / 10000
//                productInfo.priceCurrencyCode = skuInfo.getPriceCurrencyCode()
//                productInfo.channel = skuInfo.payChannel.value
                displayProductList.add(productInfo)
            }
        }
        return displayProductList
    }

    private suspend fun createOrder(productInfo: RechargeProduct): Rlt<String> {
        val rlt = walletHttpService.prePurchase(PrePurchaseReq(productInfo.id))
        when (rlt) {
            is Rlt.Failed -> {
                //Rlt.Failed(PayError(PayRes.CREATE_ORDER_FAIL))
                return rlt
            }

            is Rlt.Success -> {
                val orderId = rlt.data.data?.result
                if (orderId.isNullOrEmpty()) {
                    return Rlt.Failed(PayError(PayRes.CREATE_ORDER_ID_VALID))
                }
                return Rlt.Success(orderId)
            }
        }
    }

    override suspend fun buyProduct(
        activity: Activity,
        productInfo: RechargeProduct,
        createdOrderId: String?,
    ): Rlt<Any> {
        val skuInfo = when (val skuRlt = querySku(productInfo)) {
            is Rlt.Success -> {
                skuRlt.data
            }

            is Rlt.Failed -> {
                Log.i(TAG_PAY, "buyProduct fail, for querySku fail, $skuRlt")
                return skuRlt
            }
        }

        var orderId: String? = createdOrderId
        if (orderId.isNullOrEmpty()) {
            //创建订单
            val createOrderRlt = createOrder(productInfo)
            if (createOrderRlt !is Rlt.Success) {
                Log.i(TAG_PAY, "buyProduct fail, for createOrder, $createOrderRlt")
                return createOrderRlt
            }
            orderId = createOrderRlt.data
        }
        //发起支付
        val payChannel = PayChannel.map(productInfo.productChannel)
        if (payChannel == null) {
            return Rlt.Failed(PayChannelUnKnowError())
        }
        val purchaseResult =
            getPay(payChannel).launchPaymentFlow(activity, skuInfo, orderId)
        val purchase =
            purchaseResult.purchaseInfo?.purchases?.firstOrNull { it.getSkuId() == skuInfo.getSkuId() }
        if (purchaseResult.resCode != PayRes.SUCCESS) {
            if (purchase?.isPurchased() == true) {
                //如果已购买这个商品
                return onPurchasedResult(
                    productInfo,
                    purchase,
                    orderId,
                    SOURCE_BUY
                )
            }
            return Rlt.Failed(PayError(purchaseResult.resCode))
        }
        val obfuscatedAccountId = purchase?.obfuscatedAccountId()
        if (!obfuscatedAccountId.isNullOrEmpty() && obfuscatedAccountId != AccountModule.uid.toString()) {
            //非当前账户订单不处理
            return Rlt.Failed(PayError(PayRes.PURCHASE_VALID))
        }
        if (purchase?.isPending() == true) {
            return Rlt.Failed(
                PayError(
                    PayRes.PURCHASE_PENDING,
                    msg = getCompatString(R.string.wallet_buy_pending)
                )
            )
        }
        if (purchase == null || !purchase.isPurchased()) {
            return Rlt.Failed(PayError(PayRes.PURCHASE_VALID))
        }
        return onPurchasedResult(
            productInfo,
            purchase,
            orderId,
            SOURCE_BUY
        )
    }

    private suspend fun querySku(productInfo: RechargeProduct): Rlt<SkuInfo> {
        val payChannel = PayChannel.map(productInfo.productChannel)
        if (payChannel == null) {
            return Rlt.Failed(PayChannelUnKnowError())
        }
        return when (val skuListRlt =
            getPay(payChannel).querySkuDetails(listOf(productInfo.skuId()))) {
            is Rlt.Success -> {
                val skuInfo = skuListRlt.data.getOrNull(0)
                    ?: return Rlt.Failed(PayError(PayRes.SKU_INVALID))
                Rlt.Success(skuInfo)
            }

            is Rlt.Failed -> {
                skuListRlt
            }
        }
    }

    private suspend fun onPurchasedResult(
        productInfo: RechargeProduct,
        purchase: Purchase,
        orderId: String,
        fromSource: Int,
    ): Rlt<Any> {
        if (!VERIFY_OPEN) {
            return Rlt.Failed(PayError(PayRes.VERIFY_ORDER_FAILED))
        }

        val channel =
            PayChannel.map(productInfo.productChannel) ?: return Rlt.Failed(PayChannelUnKnowError())

        //验证订单
        val innerOrderId = orderId.ifEmpty { purchase.obfuscatedProfileId() } ?: ""
        var verifySuccess = false

        loop1@ for (i in 1..VERIFY_MAX_RETRY_TIME) {
            val rlt = walletHttpService.purchaseSuccess(
                PaySuccessReq(
                    AccountModule.uid,
                    purchase.obfuscatedProfileId() ?: "",
                    productInfo.productChannel,
                    purchase.getPurchaseToken()
                )
            )
            Log.logRltI(TAG_PAY, "purchaseResult", rlt)
            if (rlt is Rlt.Success) {
                verifySuccess = true
                reportPurchaseSuccess(productInfo, innerOrderId, purchase.getOrderId())
                break@loop1
            }
        }
        if (!verifySuccess) {
            return Rlt.Failed(PayError(PayRes.VERIFY_ORDER_FAILED))
        }

        if (CONSUME_OPEN) {
            //消费订单
            val consumeRlt = getPay(channel).consume(purchase.getPurchaseToken())
        }
        walletManager.refreshWalletData()
        queryAndHandleUnDealPurchases()
        return Rlt.Success(Any())
    }

    private val reportedOrderIdSet by lazy {
        MutableStateFlow<Set<String>>(emptySet())
    }

    private fun reportPurchaseSuccess(
        productInfo: RechargeProduct,
        innerOrderId: String,
        externalOrderId: String
    ) {
        Dispatcher.wenextThreadPoolExecutor.submit {
            val addReportedSuccess = run {
                var added = false
                reportedOrderIdSet.update { old ->
                    if (externalOrderId in old) {
                        old // 已存在，不修改
                    } else {
                        added = true
                        old + externalOrderId // 不存在就新增
                    }
                }
                added
            }
            if (addReportedSuccess) {
                //非重复订单才上报
                FirstPaymentEvent().apply {
                    orderId to externalOrderId
                    paymentType to productInfo.productCurrency
                    paymentAmount to productInfo.amount
                    paymentCurrency to productInfo.priceCurrency
                    paymentPrice to productInfo.price
                    //Adjust收入上报
                    adjustRevenue to productInfo.price
                    adjustRevenueCurrency to productInfo.priceCurrency
                }.send()
            }
        }
    }

    override fun queryAndHandleUnDealPurchases() {
        queryAndHandleUnDealPurchases(PayChannel.Google)
    }

    private fun queryAndHandleUnDealPurchases(channel: PayChannel) {
        CoroutineScope(Dispatcher.WENEXT_THREAD_POOL).launch {
            val setupRlt = getPay(channel).setup()
            if (setupRlt !is Rlt.Success) {
                return@launch
            }
            //查询订单
            val purchasesRlt = getPay(channel).queryPurchases()
            Log.logRltD(TAG_PAY, "queryAndHandleUnDealPurchases", purchasesRlt)
            if (purchasesRlt !is Rlt.Success) {
                return@launch
            }
            handleUnDealPurchases(channel, purchasesRlt.data.purchases)
        }
    }

    private suspend fun handleUnDealPurchases(
        channel: PayChannel,
        purchases: List<Purchase>
    ) {
        Log.i(
            TAG_PAY,
            "handleUnDealPurchases, channel:$channel, purchases(size:${purchases.size}):${
                purchases.joinToString(
                    separator = ","
                ) { "order(${it.obfuscatedProfileId() ?: ""})" }
            }"
        )
        purchases.forEach { purchase ->
            Log.i(
                TAG_PAY,
                "handleUnDealPurchases, obfuscatedAccountId:${purchase.obfuscatedAccountId()}, isPurchased:${purchase.isPurchased()}, isAcknowledged:${purchase.isAcknowledged()}"
            )
            val obfuscatedAccountId = purchase.obfuscatedAccountId()
            if (!obfuscatedAccountId.isNullOrEmpty() && obfuscatedAccountId != AccountModule.uid) {
                //非当前账户订单不处理
                return@forEach
            }
            if (purchase.isPurchased().not()) {
                return@forEach
            }
            if (purchase.isAcknowledged()) {
                //已经确认，直接消费
                val consumeRlt = getPay(channel).consume(purchase.getPurchaseToken())
                Log.i(
                    TAG_PAY,
                    "handleUnDealPurchases, consumeRlt: $consumeRlt"
                )
                return@forEach
            }
            //未确认，重走订单验证
            val productInfo = queryProductInfoBy(channel, purchase)
            if (productInfo == null) {
                Log.i(
                    TAG_PAY,
                    "handleUnDealPurchases, queryProductInfoBy() fail"
                )
                return@forEach
            }
            Log.i(
                TAG_PAY,
                "--> UnDealPurchases:${purchases.joinToString(separator = ",") { "${it.obfuscatedProfileId()}_${it.getPurchaseToken()}" }}"
            )
            onPurchasedResult(productInfo, purchase, "", SOURCE_COMPENSATE)
        }
    }

    private suspend fun queryProductInfoBy(
        channel: PayChannel,
        purchase: Purchase
    ): RechargeProduct? {
        val rlt = getAllRechargeList(channel)
        when (rlt) {
            is Rlt.Failed -> {
                return null
            }

            is Rlt.Success -> {
                var product = rlt.data[Currency.COIN]?.find {
                    it.skuId() == purchase.getSkuId()
                }
                if (product != null) {
                    return product
                }
                product = rlt.data[Currency.DIAMOND]?.find {
                    it.skuId() == purchase.getSkuId()
                }
                return product
            }
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        getPay(PayChannel.Google).onActivityResult(requestCode, resultCode, intent)
    }

    override fun onPendingPurchaseResult(result: PurchaseResult) {
        if (result.resCode == PayRes.SUCCESS && result.purchaseInfo?.purchases?.isNotEmpty() == true) {
            CoroutineScope(Dispatcher.WENEXT_THREAD_POOL).launch {
                handleUnDealPurchases(PayChannel.Google, result.purchaseInfo.purchases)
            }
        }
    }

}