//
//  VersionResolver.swift
//  ylgamehall
//
//  把远端 RemoteConfig 化简为 7 个目标字段（appVersion / appDownload /
//  gameVersion / gameZip / showmessage / audioDomain / audioBucket）的纯函数实现。
//
//  契约结构（参 daoqi `NewRootVC.m:689-810` 主路径，扩展为 5 层 fallback）：
//    root（RemoteConfig 顶层）
//      → agentlist[agentid] → gamelist[gameid]
//      → channellist[channelid] → marketlist[marketid]
//
//  查找语义：从最深层（market）向上回退取第一个非空值，"根"（顶层）作为最浅一层
//  自动包含在链中 —— 等价于 msext 那段从根向下"非空即覆盖"的 if 链，但写法更直接、
//  可复用，且天然支持顶层声明字段（任何字段都能放顶层兜底）。
//
//  统一接口：所有 7 个字段都用同一套 `pickString` / `pickInt` 倒序查找，
//  无任何字段级特殊兜底逻辑（如 `?? config.xxx`），所有字段平等。
//

import Foundation

// MARK: - 决策输出

/// 4 层 fallback 解析后的所有目标字段（所有字段统一走同一套 chain + pickString/Int）。
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?
    /// 七牛 CDN 域名（不带 http:// 前缀），录音上传后拼公开访问 URL 用。nil 表示远端未指定。
    public let audioDomain: String?
    /// 七牛 bucket 名（putPolicy.scope）。nil 表示远端未指定。
    public let audioBucket: String?
}

// MARK: - 节点协议（4 个层级共用同一查找接口）

/// `Agent` / `Game` / `Channel` / `Market` 都暴露相同字段集合，
/// 由 VersionResolver 用同一套倒序 fallback 算法统一处理。
private protocol RemoteConfigNode {
    var appVersion: String? { get }
    var appDownload: String? { get }
    var gameVersion: String? { get }
    var gameZip: String? { get }
    var showmessage: String? { get }
    var audioDomain: String? { get }
    var audioBucket: String? { get }
}

extension Agent: RemoteConfigNode {}
extension Game: RemoteConfigNode {}
extension Channel: RemoteConfigNode {}
extension Market: RemoteConfigNode {}
// RemoteConfig 顶层也作为节点参与 fallback 链，处于最浅一层（"根"）。
// 这样所有字段的 fallback 完全统一：节点链倒序找第一个非空 → 链上即包含根。
extension RemoteConfig: RemoteConfigNode {}

// MARK: - VersionResolver

public enum VersionResolver {

    /// 4 层 fallback 解析。
    ///
    /// - Parameters:
    ///   - config: 远端 RemoteConfig
    ///   - agentId/channelId/marketId/gameId: 当前渠道注入值（BundleConfig）
    /// - Returns: 5 字段决策结果。任一字段缺失返回 0 / nil。
    public static func resolve(
        config: RemoteConfig,
        agentId: String,
        channelId: String,
        marketId: String,
        gameId: String
    ) -> ResolvedVersion {
        let chain = buildChain(
            config: config,
            agentId: agentId,
            gameId: gameId,
            channelId: channelId,
            marketId: marketId
        )

        // 所有 7 字段统一走"chain 倒序找第一个非空"，无任何字段级特殊兜底。
        // chain 头部就是 RemoteConfig 顶层（"根"），最深层赢、根作为最浅层兜底。
        return ResolvedVersion(
            appVersion:  pickInt(chain,    \.appVersion),
            appDownload: pickString(chain, \.appDownload),
            gameVersion: pickInt(chain,    \.gameVersion),
            gameZip:     pickString(chain, \.gameZip),
            showmessage: pickString(chain, \.showmessage),
            audioDomain: pickString(chain, \.audioDomain),
            audioBucket: pickString(chain, \.audioBucket)
        )
    }

    // MARK: - 节点链匹配

    /// 沿 root → agent → game → channel → market 顺序逐层匹配，遇到不匹配的层立刻截断。
    /// 链头始终是 RemoteConfig 顶层（"根"），作为最浅一层兜底。
    ///
    /// 返回数组从根到叶有序，长度 1..5：
    /// - `[root]`                              ：agent 未匹配（链最短为根）
    /// - `[root, agent]`                       ：agent 命中、game 未匹配
    /// - `[root, agent, game]`                 ：再深一层未命中
    /// - ...
    /// - `[root, agent, game, channel, market]`：全链命中
    private static func buildChain(
        config: RemoteConfig,
        agentId: String,
        gameId: String,
        channelId: String,
        marketId: String
    ) -> [RemoteConfigNode] {
        var chain: [RemoteConfigNode] = [config]

        guard let agent = config.agentlist?.first(where: { $0.agentid == agentId })
        else { return chain }
        chain.append(agent)

        guard let game = agent.gamelist?.first(where: { $0.gameid == gameId })
        else { return chain }
        chain.append(game)

        guard let channel = game.channellist?.first(where: { $0.channelid == channelId })
        else { return chain }
        chain.append(channel)

        guard let market = channel.marketlist?.first(where: { $0.marketid == marketId })
        else { return chain }
        chain.append(market)

        return chain
    }

    // MARK: - 字段倒序 fallback（最深层赢）

    /// 从最深层（market）向根（agent）回退，取第一个非空字符串。
    private static func pickString(
        _ chain: [RemoteConfigNode],
        _ keyPath: KeyPath<RemoteConfigNode, String?>
    ) -> String? {
        for node in chain.reversed() {
            if let s = node[keyPath: keyPath], !s.isEmpty { return s }
        }
        return nil
    }

    /// 从最深层向根回退，取第一个能解析为非零整数的字段值。
    /// 字段在 JSON 里可能是 string / int / double 之一（详见 `decodeFlexibleStringIfPresent`），
    /// 解析失败 / 为 0 视同未提供继续向上找。
    private static func pickInt(
        _ chain: [RemoteConfigNode],
        _ keyPath: KeyPath<RemoteConfigNode, String?>
    ) -> Int {
        for node in chain.reversed() {
            if let s = node[keyPath: keyPath], let v = Int(s), v != 0 { return v }
        }
        return 0
    }
}
