//
//  IAPCore.swift
//  LegendTeam
//
//  Created by dong on 2021/12/22.
//

import Foundation
import StoreKit

typealias IdsCallbackBlcok = ((_ iapIds: [String]) -> Void)
class IAPCore: NSObject {
    public static let shared = IAPCore()

    var iapProducts: [SKProduct] = [SKProduct]()

    var appProducts: [IAPProducts] = [IAPProducts]()

    var buyingTransModel: PaymentTransactionModel?
    var buyingSKProduct: SKProduct? // 临时记录要买的商品（苹果实例）

    var isContinueBuyProductId: String?
    var isContinueBuyTradeId: String?

    var refreshedProductsFromIapBlock: IdsCallbackBlcok?

    /// 未登录下，持久化票据，等待用户登录后去校验
    var toBeCheckTransaction: SKPaymentTransaction?

    // keychain
    var keychainStore: EGIAPKeyChainStore = EGIAPKeyChainStore(service: KeychainServiceKey, group: nil)

    override init() {
        super.init()
        NotificationCenter.default.addObserver(self, selector: #selector(notiLoginSuccess), name: AppNotificationName.userLoginSuccess.notificationName, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    // MARK: - public
    // 🔥🔥需在AppDeletgate中调用此方法
    func check() {
        SKPaymentQueue.default().add(self)
    }

    public func requestProducts(_ products: [IAPProducts], resultblock block: IdsCallbackBlcok?) {
        appProducts = products

        if products.count == 0 {
            block?([])
            return
        }
        
        // AppStore.canMakePayments: iOS 15+
        if SKPaymentQueue.canMakePayments() {
            refreshedProductsFromIapBlock = block
            var requestIds = [String]()
            for per in products {
                if let productId = per.productId{
                    requestIds.append(productId)
                }
            }

            let skRequest = SKProductsRequest(productIdentifiers: Set(requestIds))
            skRequest.delegate = self
            skRequest.start()

        } else {
            // 用户禁止应用内付费购买。 toast。。。
            dlog("💰❌用户禁止应用内付费购买")
        }
    }

    public func addPayProductId(productId: String, tradeId: String) {
        let iaps = iapProducts
        guard iaps.count > 0 else {
            isContinueBuyProductId = productId
            isContinueBuyTradeId = tradeId
            return
        }

        var tryBuySKProduct: SKProduct?
        for per in iaps {
            if per.productIdentifier == productId {
                tryBuySKProduct = per
                buyingSKProduct = per
            }
        }

        if let buySK = tryBuySKProduct {
            Hud.showIndicator()
            addPayment(buySk: buySK, tradeId: tradeId)
        } else {
            dlog("商品不存在")
            buyingSKProduct = nil
        }
    }

    // MARK: helper

    func addPayment(buySk: SKProduct, tradeId: String) {
        if !UserCore.shared.checkUserLoginIfNotPushUserToLogin() {
            return
        }

        let payment: SKPayment = SKPayment(product: buySk)
        SKPaymentQueue.default().add(payment)

        // 持久化购买

        let transModel = PaymentTransactionModel()
        transModel.tradeNo = tradeId
        transModel.productId = buySk.productIdentifier
        transModel.tradeTime = Date().timeStamp
        buyingTransModel = transModel
        if let userId = UserCore.shared.user?.userId {
            transModel.userId = userId
        }

        keychainStore.storePreProduct(transModel.productId, tradeId: transModel.tradeNo)
    }

    private func finishATransation(transaction: SKPaymentTransaction?) {
        guard let trans = transaction else {
            return
        }

        if trans.transactionState == .purchasing {
            return
        }

        SKPaymentQueue.default().finishTransaction(trans)
    }

    private func restoreTransaction(transaction: SKPaymentTransaction?) {
        // nothing
    }

    private func transactionPurchased(transaction: SKPaymentTransaction?) {
        guard let trans = transaction, let transId = trans.transactionIdentifier else {
            return
        }

        var model: PaymentTransactionModel?
        let userId = UserCore.shared.user?.userId ?? 0
        if let buyingTrans = buyingTransModel { // 正在操作的购买
            model = buyingTrans
            buyingTrans.transactionId = transId

            keychainStore.savePaymentTransactionModel([buyingTrans], forUserId: userId)

        } else { // 一般 是监听到的购买信息(重启后).需要重新去验证
            model = keychainStore.findPaymentTransactionModel(withTransactionId: transId, forUserId: userId)
            if model == nil {
                let result = keychainStore.findAndMatchTradeToStartVerify(byProudctId: trans.payment.productIdentifier, bindTransactionId: transId, forUserId: userId)

                if result {
                    model = keychainStore.findPaymentTransactionModel(withTransactionId: transId, forUserId: userId)
                }
            }
        }
        
        guard let productId = model?.productId else{
            assert(false)
            return
        }

        if productId.contains("coin_ios") {
            verifyPurchased(purchased: model, transtion: trans)
        }else if productId.contains("vip"){
            verifyAutoRenewPurchased(purchased: model, transtion: trans)
        }
        else{
            dlog("❌❌❌不支持的transaction：\(String(describing: transaction))");
            // 【测试用】关掉交易，一般是注释掉的
            finishATransation(transaction: trans)
        }
    }

    /// 向后端服务器验证购买buff商品
    private func verifyPurchased(purchased: PaymentTransactionModel?, transtion: SKPaymentTransaction?) {
        guard let model = purchased, model.tradeNo.count > 0, let transId = transtion?.transactionIdentifier, transId.count > 0 else {
            return
        }

        if UserCore.shared.isLogin() == false {
            toBeCheckTransaction = transtion
            return
        }

        guard let receiptURL = Bundle.main.appStoreReceiptURL else {
            return
        }
        // let isSandbox = receiptURL.absoluteString.hasSuffix("sandboxReceipt")
        let receiptData = try! Data(contentsOf: receiptURL)
        let recieptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))

        var params = [String:Any]()
        
        let transactions = [transId:model.tradeNo]
        params.updateValue(recieptString, forKey: "receipt")
        params.updateValue(transactions, forKey: "transactions")
        Hud.showIndicator()
        // 上报票据
        WalletProvider.request(.iapUploadCoinReceipt(params: params), modelType: EmptyModel.self) {[weak self] result in
            Hud.hideIndicator()
            switch result {
            case .success:
                self?.doWorkAfterVerify(transaction: transtion, tradeNoLT: model.tradeNo, isVIPAutoRenew: false)
            case .failure:
                self?.doAlertUser()
                break
            }
        }
        
        /*
        PayProvider.request(.verifyiapBuy(receipt: recieptString, signature: "", transaction: transId, tradeNo: model.tradeNo), modelType: String.self) { [weak self] result in
            Hud.hideInidcator()
            switch result {
            case .success:
                self?.doWorkAfterVerify(transaction: transtion, tradeNoLT: model.tradeNo)
                break
            case .failure:
                self?.doAlertUser()
                break
            }
        }
         */
    }
    
    private func verifyAutoRenewPurchased(purchased: PaymentTransactionModel?, transtion: SKPaymentTransaction?) {
        guard let model = purchased, model.tradeNo.count > 0, let transId = transtion?.transactionIdentifier, transId.count > 0 else {
            return
        }

        if UserCore.shared.isLogin() == false {
            toBeCheckTransaction = transtion
            return
        }
        
        guard let receiptURL = Bundle.main.appStoreReceiptURL else {
            return
        }
        
        let receiptData = try! Data(contentsOf: receiptURL)
        let recieptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))

        var params = [String:Any]()
        
        params.updateValue(recieptString, forKey: "receipt")
        params.updateValue(model.productId, forKey: "productId")
        
        Hud.showIndicator()
        // 上报票据
        WalletProvider.request(.iapUploadSubscribeAppleReceipt(params: params), modelType: EmptyModel.self) {[weak self] result in
            Hud.hideIndicator()
            switch result {
            case .success:
                self?.doWorkAfterVerify(transaction: transtion, tradeNoLT: model.tradeNo, isVIPAutoRenew: true)
            case .failure:
                self?.doAlertUser()
                break
            }
        }
        
    }

    // MARK: noti

    @objc func notiLoginSuccess() {
        transactionPurchased(transaction: toBeCheckTransaction)
    }

    // MARK: - helper

    private func doWorkAfterVerify(transaction: SKPaymentTransaction?, tradeNoLT: String?, isVIPAutoRenew: Bool = false) {
        guard let userId = UserCore.shared.user?.userId, let trans = transaction, let transId = trans.transactionIdentifier, let tradeNo = tradeNoLT else {
            return
        }
        keychainStore.deletePaymentTransactionModel(withTransactionId: transId, forUserId: userId)
        finishATransation(transaction: trans)

        
        WalletCore.shared.refreshWallet(block: nil)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            if isVIPAutoRenew {
                UserCore.shared.refreshUserInfo{ result in
                    NotificationCenter.post(name: .vipStateChange, object: tradeNo, userInfo: nil)
                }
            } else {
                NotificationCenter.post(name: .chargeDonePushTradeId, object: tradeNo, userInfo: nil)
            }
            
            
        }

        // Adjust
//        if buyingSKProduct != nil {
//            if let currencyCode = buyingSKProduct?.priceLocale.currencyCode, let priceDouble = buyingSKProduct?.price.doubleValue {
//                #warning("comfirm")
//                //AdjustEvent.eventPayment(price: priceDouble, currencyCode: currencyCode, transactionId: transId)
//            }
//        }

        buyingTransModel = nil
    }

    private func doAlertUser() {
//        let alert = Alert(title: R.string.localizable.notice.localized(), text: R.string.localizable.charge_not_verify_correctly_desc.localized())
//        let gotAction = AlertAction(title: R.string.localizable.gotit.localized(), actionStyle: .confirm, block: nil)
//        alert.addAction(gotAction)
//        alert.show()
        
        let alert = Alert(title: "Tips", text: "After the Coins is charged, it may take some time to arrive, please pay attention to the wallet details.")
        let action1 = AlertAction(title: "Got it", actionStyle: .confirm) {
            
        }
        alert.addAction(action1)
        alert.show()
    }
}

extension IAPCore: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        dlog("--------------收到产品反馈消息---------------------")
        DispatchQueue.main.async {
            // Hud hideall hud
            Hud.hideIndicator()
        }

        let iapPs = response.products
        iapProducts = iapPs

        if iapPs.count == 0 {
            dlog("--------------没有商品------------------")
            DispatchQueue.main.async { [weak self] in
                // hud show Recharge is not available, please try again later
                Hud.toast(str: "Recharge is not available, please try again later")
                self?.refreshedProductsFromIapBlock?([])
            }

            return
        }

        var iapProductIdsArrayM = [String]()
        for appPer in appProducts {
            for skPer in iapProducts {
                if skPer.productIdentifier == appPer.productId {
                    let formatter = NumberFormatter()
                    formatter.numberStyle = .currency // .CurrencyStyle
                    formatter.locale = skPer.priceLocale
                    let currencyString = formatter.currencySymbol//internationalCurrencySymbol
                    appPer.currencySymbol = currencyString ?? ""
                    appPer.payAmount = CGFloat(skPer.price.floatValue)
                    iapProductIdsArrayM.append(skPer.productIdentifier)
                    continue
                }
            }
        }
        
        DispatchQueue.main.async {[weak self] in
            self?.refreshedProductsFromIapBlock?(iapProductIdsArrayM)
        }

        // 继续购买
        if let tradeId = isContinueBuyTradeId, tradeId.count > 0, let productId = isContinueBuyProductId, productId.count > 0 {
            addPayProductId(productId: productId, tradeId: tradeId)
            isContinueBuyTradeId = nil
            isContinueBuyProductId = nil
        }
    }

    func request(_ request: SKRequest, didFailWithError error: Error) {
        dlog("------------------错误-----------------:\(error)")
        DispatchQueue.main.async {
            Hud.hideIndicator()
            Hud.toast(str: "\(error.localizedDescription)")
        }
    }

    func requestDidFinish(_ request: SKRequest) {
        dlog("------------反馈信息结束-----------------")
    }
}

// MARK: - 💰💰💰 SKPaymentTransactionObserver 💰💰💰

extension IAPCore: SKPaymentTransactionObserver {
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        let userId = UserCore.shared.user?.userId ?? 0
        for trans in queue.transactions {
            switch trans.transactionState {
            case .purchasing:
//                #warning("hud show in mainqueue")
                DispatchQueue.main.async {
                    Hud.showIndicator()
                }
                dlog("->💰🚀purchasing....")
                break
            case .purchased:
                transactionPurchased(transaction: trans)
                break
            case .failed:
                DispatchQueue.main.async {
                    Hud.hideIndicator()
                }
                dlog("->💰⚠️Purchase failed....\n⚠️error:\(trans.error?.localizedDescription ?? "") \n⚠️transactionId: \(trans.transactionIdentifier ?? "")")

                if trans.error != nil {
                    guard let transId = trans.transactionIdentifier else {
                        dlog("💰⚠️⚠️⚠️ Finish掉正确取消或失败的订单 ⚠️⚠️⚠️")
                        finishATransation(transaction: trans)
                        return
                    }

                    if transId.count == 0 || keychainStore.findPaymentTransactionModel(withTransactionId: trans.transactionIdentifier!, forUserId: userId).tradeNo.count == 0  {
                        dlog("💰⚠️⚠️⚠️ Finish掉正确取消或失败的订单 ⚠️⚠️⚠️")
                        finishATransation(transaction: trans)
                    }
                }
                break
            case .restored:
                restoreTransaction(transaction: trans)
                break
            default:
                break
            }
        }
    }
}
