//
//  LNGiftManager.swift
//  Gami
//
//  Created by OneeChan on 2026/3/24.
//

import Foundation


private struct LNGiftCacheList: Codable {
    var list: [LNGiftResource]
    var version: String
}


class LNGiftManager {
    static let shared = LNGiftManager()
    
    private let pageSize = 100
    private let cacheURL = URL.cacheDir
        .appendingPathComponent("Gift", isDirectory: true)
        .appendingPathComponent("gift_resources.json")
    
    private var resourceMap: [String: LNGiftResource] = [:]
    private var resourceVersion: String = ""
    
    private var isRefreshing = false
    
    private init() {
        DispatchQueue.global().async { [weak self] in
            guard let self else { return }
            loadCache()
        }
        LNEventDeliver.addObserver(self)
    }
    
    func resource(for id: String) -> LNGiftResource? {
        resourceMap[id]
    }
    
    func fetchGiftList(roomId: String, queue: DispatchQueue = .main, handler: @escaping ([LNGiftItemVO]?) -> Void) {
        LNHttpManager.shared.loadGiftList(roomId: roomId) { [weak self] res, err in
            guard let self else { return }
            let list = res?.list.filter { self.resourceMap[$0.resId] != nil }
            queue.asyncIfNotGlobal {
                handler(list)
            }
            if let list, list.count != res?.list.count {
                updateGiftResource()
            }
            if let err {
                showToast(err.errorDesc)
            }
        }
    }
}

extension LNGiftManager {
    func updateGiftResource() {
        guard !isRefreshing else { return }
        
        var changedGifts: [LNGiftResource] = []
        var newVersion = ""
        isRefreshing = true
        
        func _fetchResource(next: String) {
            LNHttpManager.shared.loadResourceList(version: resourceVersion, size: pageSize, next: next) { [weak self] res, err in
                guard let self else { return }
                guard let res else {
                    isRefreshing = false
                    return
                }
                if !res.list.isEmpty {
                    changedGifts.append(contentsOf: res.list)
                }
                if !res.version.isEmpty {
                    newVersion = res.version
                }
                if res.list.isEmpty || res.next.isEmpty != false {
                    mergeChangedResources(changedGifts, version: newVersion)
                    isRefreshing = false
                } else {
                    _fetchResource(next: next)
                }
            }
        }
        _fetchResource(next: "")
    }
    
    private func mergeChangedResources(_ changedList: [LNGiftResource], version: String) {
        guard !changedList.isEmpty, !version.isEmpty else { return }
        
        var newMap = resourceMap
        changedList.forEach { item in
            guard !item.id.isEmpty else { return }
            newMap[item.id] = item
        }
        
        resourceMap = newMap
        resourceVersion = version
        
        saveCache()
    }
}
 
extension LNGiftManager {
    private func loadCache() {
        guard let data = try? Data(contentsOf: cacheURL) else { return }
        
        do {
            let snapshot = try JSONDecoder().decode(LNGiftCacheList.self, from: data)
            resourceVersion = snapshot.version
            resourceMap = snapshot.list.reduce(into: [:]) { partialResult, item in
                guard !item.id.isEmpty else { return }
                partialResult[item.id] = item
            }
        } catch {
            Log.e("load gift resource cache failed: \(error.localizedDescription)")
        }
    }
    
    private func saveCache() {
        let snapshot = LNGiftCacheList(
            list: Array(resourceMap.values),
            version: resourceVersion
        )
        
        do {
            let folderURL = cacheURL.deletingLastPathComponent()
            try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
            let data = try JSONEncoder().encode(snapshot)
            try data.write(to: cacheURL, options: .atomic)
        } catch {
            Log.e("save gift resource cache failed: \(error.localizedDescription)")
        }
    }
}

extension LNGiftManager {
    func sendGift(params: LNSendGiftParams, queue: DispatchQueue = .main,
                  handler: @escaping (Bool) -> Void) {
        let remain: TimeInterval = LNUserDefaults[.remainExchange, 0]
        if remain.isSameDay {
            params.seamlessRedeem = true
        }
        
        LNHttpManager.shared.sendGift(params: params) { res, err in
            queue.asyncIfNotGlobal {
                handler(err == nil)
            }
            if let err {
                showToast(err.errorDesc)
                if case .serverError(let code, _) = err {
                    runOnMain {
                        if code == LNOrderErrorCode.NotEnoughMoney.rawValue {
                            let panel = LNMoneyNotEnoughAlertView()
                            panel.update(.diamond)
                            panel.popup()
                        } else if LNOrderErrorCode.NotEnoughMoneyButCanExchange.rawValue == code {
                            let panel = LNMoneyNotEnoughAlertView()
                            panel.update(.diamond, exchange: .coin)
                            panel.exchangeHandler = {
                                params.seamlessRedeem = true
                                self.sendGift(params: params, queue: queue, handler: handler)
                            }
                            panel.popup()
                        }
                    }
                }
            }
            if let res {
                LNPurchaseManager.shared.updateWallet(diamond: res.diamond, coin: res.goldcoin)
            }
        }
    }
}

extension LNGiftManager: LNAccountManagerNotify {
    func onUserLogin() {
        updateGiftResource()
    }
}
