//
//  VersionResolver.swift
//  ylgamehall
//
//  把远端 RemoteConfig 化简为 5 个目标字段（appVersion/appDownload/gameVersion/
//  gameZip/showmessage）的纯函数实现，照搬 msext chulishengji 双子树合并算法。
//  详见 docs/Development-Plan.md ADR-008-D / Design §6.3.2 / msext NewRootVC.m:1372-1538。
//

import Foundation

// MARK: - 决策输出

/// chulishengji 双子树合并后的 5 个目标字段。
public struct ResolvedVersion: Sendable, Equatable {
    /// 远端期望的原生 IPA 版本（与 BundleConfig.appVersion 比较）。0 表示远端未指定。
    public let appVersion: Int
    /// IPA 升级跳转的 Safari URL。nil 表示远端未指定。
    public let appDownload: String?
    /// 远端期望的 H5 zip 版本（与本地 version.xml /game/version@value 比较）。
    public let gameVersion: Int
    /// H5 zip 下载 URL。nil 表示远端未指定。
    public let gameZip: String?
    /// 运营公告。非空时阻塞后续升级判断（msext 杀手锏 #2）。
    public let showmessage: String?
}

// MARK: - 内部：单条子树的提取结果

/// 单条子树（agent 子树 或 game 子树）4 层逐层覆盖后的 5 字段。
private struct SubtreeExtract {
    var appVersion: Int = 0
    var appDownload: String?
    var gameVersion: Int = 0
    /// 注意字段名：agent 子树原项目用 `game_download`，game 子树用 `game_zip`，
    /// 两者本质同一字段，新外壳两 key 都查（择一非空）。
    var gameZip: String?
    var showmessage: String?
}

// MARK: - VersionResolver

public enum VersionResolver {

    /// chulishengji 双子树合并（msext `NewRootVC.m:1372-1538`）。
    ///
    /// 1. 从 agent 子树提取（`agent → channel → market → game`，game 嵌在 market 命中后才遍历）
    /// 2. 从 game 子树提取（`game → game-self → channel → market`）
    /// 3. **IPA 合并**：默认 agent 赢；`gameConfig.appVersion > agentConfig.appVersion` 时
    ///    game 赢（NewRootVC.m:1416）
    /// 4. **zip 合并**：默认 game 赢；`agentConfig.gameVersion > gameConfig.gameVersion` 时
    ///    agent 赢（NewRootVC.m:1456）
    /// 5. **showmessage**：两条子树多层多次覆盖，最后写赢；若都为 nil，回退到顶层 `config.showmessage`
    ///
    /// - Parameters:
    ///   - config: 远端 RemoteConfig（Codable 解析后）
    ///   - agentId: 当前 BundleConfig.agent
    ///   - channelId: 当前 BundleConfig.channel
    ///   - marketId: 当前 BundleConfig.market
    ///   - gameId: 当前 BundleConfig.gameId
    /// - Returns: 5 字段决策结果。任一字段缺失返回 0 / nil。
    public static func resolve(
        config: RemoteConfig,
        agentId: String,
        channelId: String,
        marketId: String,
        gameId: String
    ) -> ResolvedVersion {
        // 找当前 agent
        guard let agent = config.agentlist?.first(where: { $0.agentid == agentId }) else {
            // agent 找不到匹配（极端情况）：返回 0 / nil，让 WebContainer 走"无升级"路径
            return ResolvedVersion(
                appVersion: 0, appDownload: nil,
                gameVersion: 0, gameZip: nil,
                showmessage: config.showmessage
            )
        }

        // 找当前 agent 下的 game
        let game = agent.gamelist?.first(where: { $0.gameid == gameId })

        let agentExtract = extractAgentSubtree(
            agent: agent,
            channelId: channelId,
            marketId: marketId,
            gameId: gameId
        )

        let gameExtract: SubtreeExtract = game.map { g in
            extractGameSubtree(game: g, channelId: channelId, marketId: marketId)
        } ?? SubtreeExtract()

        let (appVer, appDL) = mergeIPA(agent: agentExtract, game: gameExtract)
        let (gameVer, gameZip) = mergeZip(agent: agentExtract, game: gameExtract)

        // showmessage：game 子树 > agent 子树 > 顶层（任意非空就用，最后写赢）
        let msg = gameExtract.showmessage
            ?? agentExtract.showmessage
            ?? config.showmessage

        return ResolvedVersion(
            appVersion: appVer,
            appDownload: appDL,
            gameVersion: gameVer,
            gameZip: gameZip,
            showmessage: msg
        )
    }

    // MARK: - agent 子树（getagentversion，NewRootVC.m:885-1013）

    /// agent 子树 4 层：agent → channel → market → game
    /// （注意：game 嵌在 market 命中后才遍历 market.gamelist；与 game 子树的结构不同）
    private static func extractAgentSubtree(
        agent: Agent,
        channelId: String,
        marketId: String,
        gameId: String
    ) -> SubtreeExtract {
        var r = SubtreeExtract()

        // 层 1：agent 自身
        applyOnto(&r, from: agent)

        // 层 2：channel
        guard let channel = agent.channellist?.first(where: { $0.channelid == channelId })
        else { return r }
        applyOnto(&r, from: channel)

        // 层 3：market
        guard let market = channel.marketlist?.first(where: { $0.marketid == marketId })
        else { return r }
        applyOnto(&r, from: market)

        // 层 4：game（嵌在 market 命中后才遍历，msext 历史结构）
        // 注：Market 模型当前不带 gamelist 字段，原项目这层在 channel 节点的 marketlist
        // 内部又嵌了一份 gamelist。如服务端有这层数据需要扩展 Market.gamelist。
        // 当前 RemoteConfig 模型未声明该字段，暂跳过——若上线时发现该层有版本数据需补。

        return r
    }

    // MARK: - game 子树（getgameversion，NewRootVC.m:1015-1180）

    /// game 子树 4 层：game → game-self → channel → market
    /// （第 2 层是把 game 自身当作 infotwo 再嵌一遍 channellist；msext 那段曾经的
    /// agentid 判断被注释掉了，等价于直接走 game.channellist）
    private static func extractGameSubtree(
        game: Game,
        channelId: String,
        marketId: String
    ) -> SubtreeExtract {
        var r = SubtreeExtract()

        // 层 1：game 自身
        applyOnto(&r, from: game)

        // 层 2：game-self（理论上同 game.channellist，等价于层 3 入口，故略）

        // 层 3：channel
        guard let channel = game.channellist?.first(where: { $0.channelid == channelId })
        else { return r }
        applyOnto(&r, from: channel)

        // 层 4：market
        guard let market = channel.marketlist?.first(where: { $0.marketid == marketId })
        else { return r }
        applyOnto(&r, from: market)

        return r
    }

    // MARK: - 字段级合并

    /// IPA 合并：默认 agent 赢；game 版本号更大时 game 赢（NewRootVC.m:1408-1447）
    private static func mergeIPA(
        agent: SubtreeExtract,
        game: SubtreeExtract
    ) -> (Int, String?) {
        // 都有：取较大版本对应的下载链
        if agent.appDownload != nil && game.appDownload != nil {
            if game.appVersion > agent.appVersion {
                return (game.appVersion, game.appDownload)
            }
            return (agent.appVersion, agent.appDownload)
        }
        // 仅一边：取那边
        if let dl = agent.appDownload {
            return (agent.appVersion, dl)
        }
        if let dl = game.appDownload {
            return (game.appVersion, dl)
        }
        return (0, nil)
    }

    /// zip 合并：默认 game 赢；agent 版本号更大时 agent 赢（NewRootVC.m:1450-1492）
    private static func mergeZip(
        agent: SubtreeExtract,
        game: SubtreeExtract
    ) -> (Int, String?) {
        if agent.gameZip != nil && game.gameZip != nil {
            if agent.gameVersion > game.gameVersion {
                return (agent.gameVersion, agent.gameZip)
            }
            return (game.gameVersion, game.gameZip)
        }
        if let zip = game.gameZip {
            return (game.gameVersion, zip)
        }
        if let zip = agent.gameZip {
            return (agent.gameVersion, zip)
        }
        return (0, nil)
    }

    // MARK: - 单层逐字段覆盖工具

    /// 从单层节点向 SubtreeExtract 累加：非空字段才覆盖，等价于 msext 的
    /// `if (xxx != "" && xxx != nil) self.xxx = xxx` 语义。
    private static func applyOnto(_ r: inout SubtreeExtract, from agent: Agent) {
        if let v = Int(agent.appVersion ?? ""), v != 0 { r.appVersion = v }
        if let s = agent.appDownload, !s.isEmpty { r.appDownload = s }
        if let v = Int(agent.gameVersion ?? ""), v != 0 { r.gameVersion = v }
        if let s = agent.gameZip, !s.isEmpty { r.gameZip = s }
        if let s = agent.showmessage, !s.isEmpty { r.showmessage = s }
    }
    private static func applyOnto(_ r: inout SubtreeExtract, from channel: Channel) {
        if let v = Int(channel.appVersion ?? ""), v != 0 { r.appVersion = v }
        if let s = channel.appDownload, !s.isEmpty { r.appDownload = s }
        if let v = Int(channel.gameVersion ?? ""), v != 0 { r.gameVersion = v }
        if let s = channel.gameZip, !s.isEmpty { r.gameZip = s }
        if let s = channel.showmessage, !s.isEmpty { r.showmessage = s }
    }
    private static func applyOnto(_ r: inout SubtreeExtract, from market: Market) {
        if let v = Int(market.appVersion ?? ""), v != 0 { r.appVersion = v }
        if let s = market.appDownload, !s.isEmpty { r.appDownload = s }
        if let v = Int(market.gameVersion ?? ""), v != 0 { r.gameVersion = v }
        if let s = market.gameZip, !s.isEmpty { r.gameZip = s }
        if let s = market.showmessage, !s.isEmpty { r.showmessage = s }
    }
    private static func applyOnto(_ r: inout SubtreeExtract, from game: Game) {
        if let v = Int(game.appVersion ?? ""), v != 0 { r.appVersion = v }
        if let s = game.appDownload, !s.isEmpty { r.appDownload = s }
        if let v = Int(game.gameVersion ?? ""), v != 0 { r.gameVersion = v }
        if let s = game.gameZip, !s.isEmpty { r.gameZip = s }
        if let s = game.showmessage, !s.isEmpty { r.showmessage = s }
    }
}
