//
//  BackGameDataHandler.swift
//  ylgamehall
//
//  H5 → Native handler：backgameData — 子游戏退出并把数据回传给大厅。
//  契约：docs/H5-Native-Contract.md §3.1 ［18］
//
//  与 msext gameController.m:691-709 等价：
//    1) 停背景音（无条件，与 stopBackground 不同 — msext 不看 type 直接 nil）
//    2) AppCoordinator.shared.popSubGame(returningData: data)
//       内部 popViewController + 发 .subGameDidReturn 通知，由大厅监听后
//       回调 H5 `getWebdata` 完成反向闭环
//    3) responseCallback("backgameData") 无论 data 是否解析成功都回（msext 字面）
//
//  ⚠️ 仅注册到子游戏 SubGameViewController；大厅 WebContainerViewController 不注册。
//  H5 大厅页若误调 backgameData 会落空（bridge 无对应 handler → JS 端的 callback 直接卡），
//  msext 同款行为 — 这是契约边界，不在原生侧兜底。
//
//  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  SDK 清理钩子状态（每接入新 SDK 都必须确认或新增）：
//
//   ✅ Phase 4.E 微信：新版 SDK（2.x）无 static delegate API，逐次调用 handleOpenURL/sendAuthReq
//      时传入并被 SDK 弱引用；WeChatManager singleton 长生命周期，子游戏 pop 不析构，无需清理
//   ✅ Phase 5 高德定位：`location.stop()`（子游戏私有实例，canImport 守卫，
//      Xcode Embed AMapLocationKit 后激活）
//   ⏳ Phase 3.B/C/D 录音 / 七牛上传：`AudioRecorder.shared.cancel()` 待 opencore-amr
//      接入后取消注释；`QiniuUploader.cancelInFlight()` 依赖 Task.cancel 传播无需显式调
//   ⏳ Phase 8 Agora（若未来重启视频）：`agoraKit.leaveChannel(...)`
//      （目前 VideoRoomHandlers 是 stub 无需清理）
//  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//

import Foundation

public enum BackGameDataHandler {

    /// - Parameter location: **子游戏自己的** LocationService（msext cleanUpAction
    ///   只停 gameController 那一个 manager，不影响大厅）。
    public static func register(on bridge: any BridgeProtocol, location: LocationService) {
        // 【18】backgameData
        //   入参 data（字符串，子游戏 H5 自定义透传给大厅 getWebdata）
        //   cb: "backgameData"
        bridge.register("backgameData") { data, callback in
            await MainActor.run {
                // data 可能是 string / object，统一序列化为 string（msext NSString 透传等价）。
                // - 直接是 .string → 取字面
                // - 其它类型 → JSON 序列化（保留对象结构传给大厅 H5）
                let payload = serialize(data)

                // 子游戏 push 期间挂载的 SDK 资源在 pop 前显式清理（避免 delegate 悬挂 /
                // 后台任务残留），与 msext gameController.m:691-709 + cleanUpAction 等价。
                AudioPlayer.shared.stopAllBackground()

                // 微信 SDK 1.8.2 delegate 通过 sendAuthReq:viewController:delegate: 或
                // handleOpenURL:delegate: 逐次调用时传入并被 SDK 弱引用。WeChatManager
                // 作为 shared singleton 长生命周期持有，子游戏 pop 不会让它析构，无悬挂回调风险。
                // msext gameController.m:699 的 [WXApi setDelegate:nil] 是 1.x 早期 API，
                // 1.8.x 之后已废弃，不需要清理。

                #if canImport(AMapLocationKit)
                // msext gameController.m:2473-2478 cleanUpAction 等价
                location.stop(caller: "backgameData")
                #endif

                // Phase 3.C 录音 / Phase 3.B 七牛上传 清理（QiniuUploader.cancelInFlight 当前
                // 依赖 Task.cancel 传播；AudioRecorder 待 opencore-amr 接入后激活清理）
                // AudioRecorder.shared.cancel()   // ← Phase 3.C 接入时取消注释

                AppCoordinator.shared.popSubGame(returningData: payload)
            }
            callback?(.string("backgameData"))
        }
    }

    private static func serialize(_ data: BridgeData?) -> String {
        if let s = data?.asString { return s }
        guard let data else { return "" }
        let any = data.jsonObject
        if let bytes = try? JSONSerialization.data(withJSONObject: any, options: [.fragmentsAllowed]),
           let s = String(data: bytes, encoding: .utf8) {
            return s
        }
        return ""
    }
}
