//
//  TSPurchaseManager.swift
//  TSLiveWallpaper
//
//  Created by 100Years on 2025/1/13.
//

import Foundation
import StoreKit

public enum PremiumPeriod: String, CaseIterable {
    case none           = ""
    case month          = "Monthly"
    case year           = "Yearly"
    case lifetime       = "Lifetime"
}

public struct PurchaseProduct {
    public let productId: String
    public let period: PremiumPeriod
    
    public init(productId: String, period: PremiumPeriod) {
        self.productId = productId
        self.period = period
    }
}

public enum PremiumRequestState {
    case none
    
    case loading
    case loadSuccess
    case loadFail
    
    case paying
    case paySuccess
    case payFail
    
    case restoreing
    case restoreSuccess
    case restoreFail
    
    case verifying
    case verifySuccess
    case verifyFail
}


public extension Notification.Name {
    static let kPurchasePrepared = Self.init("kPurchaseProductPrepared")
    static let kPurchaseDidChanged = Self.init("kPurchaseDidChanged")
}

private let kFreeNumKey = "kFreeNumKey"
private let kPremiumExpiredInfoKey = "premiumExpiredInfoKey"


typealias PurchaseStateChangeHandler = (_ manager: PurchaseManager, _ state: PremiumRequestState, _ object: Any?) -> Void

let kPurchaseDefault = PurchaseManager.default
public class PurchaseManager: NSObject {
    @objc public static let `default` = PurchaseManager()

    //苹果共享密钥
    private let AppleSharedKey:String = "7fa595ea66a54b16b14ca2e2bf40f276"
    
    //商品信息
    public lazy var purchaseProducts:[PurchaseProduct] = {
        return [PurchaseProduct(productId: "101", period:.month),
                PurchaseProduct(productId: "102", period: .year),
                //PurchaseProduct(productId: "003", period: .lifetime),
        ]
    }()

    struct Config {
        static let verifyUrl = "https://buy.itunes.apple.com/verifyReceipt"
        static let sandBoxUrl = "https://sandbox.itunes.apple.com/verifyReceipt"
    }

    lazy var products: [SKProduct] = []

    var onPurchaseStateChanged: PurchaseStateChangeHandler?

    // 会员信息
    var vipInformation: [String: Any] = [:]

    
    // 免费使用会员转 livew的次数
    var freeNum:Int = 0
    
    //原始订单交易id dict
    var originalTransactionIdentifierDict:[String:String] = [:]
    
    override init() {
        super.init()

        SKPaymentQueue.default().add(self)

        if let info = UserDefaults.standard.object(forKey: kPremiumExpiredInfoKey) as? [String: Any] {
            vipInformation = info
        }
        
        initializeForFree()
    }

    public var expiredDate: Date? {
        guard let time = vipInformation["expireTime"] as? String else {
            return nil
        }
        return convertExpireDate(from: time)
    }

    public var expiredDateString: String {
        if vipType == .lifetime{
            return "Life Time"
        } else {
            if let expDate = expiredDate {
                let format = DateFormatter()
                format.locale = .current
                format.dateFormat = "yyyy-MM-dd"
                return format.string(from: expDate)
            } else {
                return "--"
            }
        }
    }

    private func convertExpireDate(from string: String) -> Date? {
        if let ts = TimeInterval(string) {
            let date = Date(timeIntervalSince1970: ts / 1000)
            return date
        }
        return nil
    }

    @objc public var isVip: Bool {
//        #if DEBUG
//        return true
//        #endif
        guard let expiresDate = expiredDate else {
            return false
        }
        let todayStart = Calendar.current.startOfDay(for: Date())
        let todayStartTs = todayStart.timeIntervalSince1970
        let expiresTs = expiresDate.timeIntervalSince1970

        return expiresTs > todayStartTs
    }

    public var vipType: PremiumPeriod {
        guard isVip, let type = vipInformation["type"] as? String else {
            return .none
        }
        return PremiumPeriod(rawValue: type) ?? .none
    }

    /// 过期时间: 1683277585000 毫秒
    func updateExpireTime(_ timeInterval: String,
                          for productId: String) {
        vipInformation.removeAll()
        vipInformation["expireTime"] = timeInterval
        vipInformation["productId"] = productId
        vipInformation["type"] = period(for: productId).rawValue

        UserDefaults.standard.set(vipInformation, forKey: kPremiumExpiredInfoKey)
        UserDefaults.standard.synchronize()

        NotificationCenter.default.post(name: .kPurchaseDidChanged, object: nil)
    }

    // 商品id对应的时间周期
    func period(for productId: String) -> PremiumPeriod {
        return purchaseProducts.first(where: { $0.productId == productId })?.period ?? .none
    }

    // 时间周期对应的商品id
    func productId(for period: PremiumPeriod) -> String? {
        return purchaseProducts.first(where: { $0.period == period })?.productId
    }
}

// MARK: 商品信息

extension PurchaseManager {
    public func product(for period: PremiumPeriod) -> SKProduct? {
        return products.first(where: { $0.productIdentifier == productId(for: period) })
    }

    // 商品价格
    public func price(for period: PremiumPeriod) -> String? {
        guard let product = product(for: period) else {
            return nil
        }
        let formatter = NumberFormatter()
        formatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4
        formatter.numberStyle = .currency
        formatter.locale = product.priceLocale
        return formatter.string(from: product.price)
    }

    
//    public func originalPrice(for period: PremiumPeriod) -> String? {
//        guard let product = product(for: period) else {
//            return nil
//        }
//        switch period {
//        case .year, .lifetime:
//            // 5折
//            let price = product.price.doubleValue
//            let calculatePrice = price * 2
//            let originStr = String(format: "%.2f", calculatePrice)
//            let originPrice = NSDecimalNumber(string: originStr, locale: product.priceLocale)
//
//            let formatter = NumberFormatter()
//            formatter.formatterBehavior = NumberFormatter.Behavior.behavior10_4
//            formatter.numberStyle = .currency
//            formatter.locale = product.priceLocale
//            return formatter.string(from: originPrice)
//        default:
//            return nil
//        }
//    }
}

// MARK: 商品 & 订阅请求

extension PurchaseManager {
    ///请求商品
    public func requestProducts() {
        if !products.isEmpty {
            purchase(self, didChaged: .loadSuccess, object: nil)
        }

        purchase(self, didChaged: .loading, object: nil)
        let productIdentifiers = Set(purchaseProducts.map({ $0.productId }))
        debugPrint("PurchaseManager requestProducts = \(productIdentifiers)")
        let request = SKProductsRequest(productIdentifiers: productIdentifiers)
        request.delegate = self
        request.start()
    }

    public func restorePremium() {
        purchase(self, didChaged: .restoreing, object: nil)
        SKPaymentQueue.default().restoreCompletedTransactions()
        debugPrint("PurchaseManager restoreCompletedTransactions")
    }

    /// 购买支付
    public func pay(for period: PremiumPeriod) {
        guard SKPaymentQueue.canMakePayments() else {
            purchase(self, didChaged: .payFail, object: "Payment failed, please check your payment account")
            return
        }

        guard SKPaymentQueue.default().transactions.count <= 0 else {
            purchase(self, didChaged: .payFail, object: "You have outstanding orders that must be paid for before a new subscription can be placed.")
            restorePremium()
            return
        }
        if let product = product(for: period) {
            purchase(self, didChaged: .paying, object: nil)
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(payment)
            debugPrint("PurchaseManager pay period = \(period)")
        }else{
            purchase(self, didChaged: .payFail, object: "Payment failed, no this item")
        }
    }
}

// MARK: 商品回调

extension PurchaseManager: SKProductsRequestDelegate {
    public func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        let products = response.products
        self.products = products
        purchase(self, didChaged: .loadSuccess, object: nil)
        NotificationCenter.default.post(name: .kPurchasePrepared, object: nil)
        debugPrint("PurchaseManager productsRequest didReceive = \(products)")
    }

    public func request(_ request: SKRequest, didFailWithError error: Error) {
        debugPrint("PurchaseManager productsRequest error = \(error)")
        purchase(self, didChaged: .loadFail, object: error.localizedDescription)
    }
}

// MARK: 订阅回调

extension PurchaseManager: SKPaymentTransactionObserver {
    public func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        debugPrint("PurchaseManager paymentQueue transactions.count = \(transactions.count)")
//        debugPrint("PurchaseManager paymentQueue transactions = \(transactions)")
        
        originalTransactionIdentifierDict.removeAll()
        // 因为只有订阅类的购买项
        for transaction in transactions {

//            debugPrint("PurchaseManager paymentQueue transactions transactionIdentifier original= \(transaction.original?.transactionIdentifier)")
//            debugPrint("PurchaseManager paymentQueue transactions transactionIdentifier = \(transaction.transactionIdentifier)")
//            debugPrint("PurchaseManager paymentQueue transactions transactionIdentifier productIdentifier = \(transaction.payment.productIdentifier)")
            
            switch transaction.transactionState {
            case .purchasing:
                // Transaction is being added to the server queue.
                purchase(self, didChaged: .paying, object: nil)

            case .purchased:
                SKPaymentQueue.default().finishTransaction(transaction)
                //同样的原始订单,只处理一次.
                guard judgeWhether(transaction: transaction) else {
                    break
                }
                
                // Transaction is in queue, user has been charged.  Client should complete the transaction.
                #if DEBUG
                    verifyPayResult(transaction: transaction, useSandBox: true)
                #else
                    verifyPayResult(transaction: transaction, useSandBox: false)
                #endif
                
                
            case .failed:
                
                SKPaymentQueue.default().finishTransaction(transaction)
                // Transaction was cancelled or failed before being added to the server queue.
                var message = "Payment Failed"
                if let error = transaction.error as? SKError,
                   error.code == SKError.paymentCancelled {
                    message = "The subscription was canceled"
                }
                purchase(self, didChaged: .payFail, object: message)

            case .restored:
                SKPaymentQueue.default().finishTransaction(transaction)
                //同样的原始订单,只处理一次.
                guard judgeWhether(transaction: transaction) else {
                    break
                }
                
                // Transaction was restored from user's purchase history.  Client should complete the transaction.
                if let original = transaction.original,
                   original.transactionState == .purchased {
                    #if DEBUG
                        verifyPayResult(transaction: transaction, useSandBox: true)
                    #else
                        verifyPayResult(transaction: transaction, useSandBox: false)
                    #endif
                } else {
                    purchase(self, didChaged: .restoreFail, object: "Failed to restore subscribe, please try again")
                }
                
            case .deferred: // The transaction is in the queue, but its final status is pending external action.
                break
            @unknown default:
                SKPaymentQueue.default().finishTransaction(transaction)
            }
        }
    }

    public func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
        purchase(self, didChaged: .restoreFail, object: nil)
    }

    public func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
        if let trans = queue.transactions.first(where: { $0.transactionState == .purchased }) {
            verifyPayResult(transaction: trans, useSandBox: false)
        } else if queue.transactions.isEmpty {
            purchase(self, didChaged: .restoreFail, object: "You don't have an active subscription")
        }
    }
    
    func judgeWhether(transaction:SKPaymentTransaction) -> Bool {
        let id = transaction.original?.transactionIdentifier
        if let id = id {
            if let value = originalTransactionIdentifierDict[id] {
                return false
            }
            originalTransactionIdentifierDict[id] = "1"
        }
        return true
    }
}

extension PurchaseManager {
    func verifyPayResult(transaction: SKPaymentTransaction, useSandBox: Bool) {
        purchase(self, didChaged: .verifying, object: nil)

        guard let url = Bundle.main.appStoreReceiptURL,
              let receiptData = try? Data(contentsOf: url) else {
            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
            return
        }

        let requestContents = [
            "receipt-data": receiptData.base64EncodedString(),
            "password": AppleSharedKey,
        ]
        guard let requestData = try? JSONSerialization.data(withJSONObject: requestContents) else {
            purchase(self, didChaged: .verifyFail, object: "凭证文件为空")
            return
        }

        let verifyUrlString = useSandBox ? Config.sandBoxUrl : Config.verifyUrl
        
        postRequest(urlString: verifyUrlString, httpBody: requestData) { [weak self] data, error in
            guard let self = self else { return }
            if let data = data,
               let jsonResponse = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
//                debugPrint("PurchaseManager verifyPayResult = \(jsonResponse)")
                let status = jsonResponse["status"]
                if let status = status as? String, status == "21007" {
                    self.verifyPayResult(transaction: transaction, useSandBox: true)
                } else if let status = status as? Int, status == 21007 {
                    self.verifyPayResult(transaction: transaction, useSandBox: true)
                } else if let status = status as? String, status == "0" {
                    self.handlerPayResult(transaction: transaction, resp: jsonResponse)
                } else if let status = status as? Int, status == 0 {
                    self.handlerPayResult(transaction: transaction, resp: jsonResponse)
                } else {
                    self.purchase(self, didChaged: .verifyFail, object: "验证结果状态码错误:\(status.debugDescription)")
                }
            } else {
                self.purchase(self, didChaged: .verifyFail, object: "验证结果为空")
                debugPrint("PurchaseManager 验证结果为空")
            }
        }
        
        /*
          21000 App Store无法读取你提供的JSON数据
          21002 收据数据不符合格式
          21003 收据无法被验证
          21004 你提供的共享密钥和账户的共享密钥不一致
          21005 收据服务器当前不可用
          21006 收据是有效的，但订阅服务已经过期。当收到这个信息时，解码后的收据信息也包含在返回内容中
          21007 收据信息是测试用（sandbox），但却被发送到产品环境中验证
          21008 收据信息是产品环境中使用，但却被发送到测试环境中验证
          */
    }

    func handlerPayResult(transaction: SKPaymentTransaction, resp: [String: Any]) {
        var isLifetime = false
        // 终生会员
        if let receipt = resp["receipt"] as? [String: Any],
           let in_app = receipt["in_app"] as? [[String: Any]] {
            if let lifetimeProductId = purchaseProducts.first(where: { $0.period == .lifetime })?.productId,
               let _ = in_app.filter({ ($0["product_id"] as? String) == lifetimeProductId }).first(where: { item in
                   if let purchase_date = item["purchase_date"] as? String,
                      !purchase_date.isEmpty {
                       return true
                   } else if let purchase_date_ms = item["purchase_date_ms"] as? String,
                             !purchase_date_ms.isEmpty {
                       return true
                   }
                   return false
               }) {
                updateExpireTime(lifetimeExpireTime, for: lifetimeProductId)
                isLifetime = true
            }
        }

        if !isLifetime {
            let info = resp["latest_receipt_info"] as? [[String: Any]]
            if let firstItem = info?.first,
               let expires_date_ms = firstItem["expires_date_ms"] as? String,
               let productId = firstItem["product_id"] as? String {
                updateExpireTime(expires_date_ms, for: productId)
            }
        }

        DispatchQueue.main.async {
            if transaction.transactionState == .restored {
                self.purchase(self, didChaged: .restoreSuccess, object: nil)
            } else {
                self.purchase(self, didChaged: .paySuccess, object: nil)
            }
        }
    }
    
    // 终生会员过期时间：100年
    var lifetimeExpireTime: String {
        let date = Date().addingTimeInterval(100 * 365 * 24 * 60 * 60)
        return "\(date.timeIntervalSince1970 * 1000)"
    }

    
    /// 发送 POST 请求
    /// - Parameters:
    ///   - urlString: 请求的 URL 字符串
    ///   - parameters: 请求的参数字典（将自动转换为 JSON）
    ///   - timeout: 超时时间（默认 30 秒）
    ///   - completion: 请求完成的回调，返回 `Data?` 和 `Error?`
    func postRequest(
        urlString: String,
        httpBody: Data?,
        timeout: TimeInterval = 90,
        completion: @escaping (Data?, Error?) -> Void
    ) {
        // 确保 URL 有效
        guard let url = URL(string: urlString) else {
            completion(nil, NSError(domain: "Invalid URL", code: -1, userInfo: nil))
            return
        }
        debugPrint("postRequest urlString=",urlString)
        // 创建请求
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.timeoutInterval = timeout
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = httpBody
  
        // 创建数据任务
        let task = URLSession.shared.dataTask(with: request) { data, response, error in
            completion(data, error)
        }
        
        // 启动任务
        task.resume()
    }
}

public extension PurchaseManager {
    func canContinue(_ requireVip: Bool) -> Bool {
        guard requireVip else {
            return true
        }
        return isVip
    }
    
    func purchase(_ manager: PurchaseManager, didChaged state: PremiumRequestState, object: Any?){
        onPurchaseStateChanged?(manager,state,object)
    }
}


/// 免费次数
extension PurchaseManager {
    /// 使用一次免费次数
    func useOnceForFree(){
        if freeNum > 0 {
            freeNum-=1
        }else{
            freeNum=0
        }
        saveForFree()
    }
    
    
    func saveForFree(){
        UserDefaults.standard.set(String(freeNum), forKey: kFreeNumKey)
        UserDefaults.standard.synchronize()
    }
    
    func initializeForFree(){
        if let num = UserDefaults.standard.string(forKey: kFreeNumKey) {
            freeNum = Int(num) ?? 3
        }else{
            freeNum = 3
            saveForFree()
        }
    }
    
    /// 免费次数是否可用
    func freeNumAvailable() -> Bool{
        if isVip == true {
            return true
        }else{
            if freeNum <= 0 {
                return false
            }else{
                return true
            }
        }
    }
}

/*
 
 首先，创建SKProductsRequest对象并使用init(productIdentifiers:)初始化，传入要查询的产品标识符。
 然后，调用start()方法开始请求产品信息。
 当请求成功时，productsRequest(_:didReceive:)方法会被调用，在这里可以获取产品详细信息并展示给用户（如在界面上显示产品价格、名称等）。如果请求失败，productsRequest(_:didFailWithError:)方法会被调用来处理错误。
 当用户决定购买某个产品后，根据产品信息（SKProduct对象）创建SKPayment对象，然后使用SKPaymentQueue的add(_:)方法将支付请求添加到支付队列。
 同时，在应用启动等合适的时机，通过SKPaymentQueue的addTransactionObserver(_:)方法添加交易观察者。当支付状态发生变化时，paymentQueue(_:updatedTransactions:)方法会被调用，在这里可以根据交易状态（如购买成功、失败、恢复等）进行相应的处理。
 
 */
