//
//  StartLocationHandler.swift
//  ylgamehall
//
//  H5 → Native handler：startlocation — 定位（Phase 5 接入高德 SDK 后激活真实路径）
//  契约：docs/H5-Native-Contract.md §3.1 ［20］ + §3.2 ［6］
//
//  完整链路：
//    1. LocationService.shared.requestOnce()（actor，包 AMapLocationManager 异步）
//    2. bridge.call("getlocationinfo", 9 字段) 反向 callback
//       - latitude / longitude 是 **string**（`String(format: "%f")`）
//       - province 小写 p（与 sharelogin.Province 大写不同）
//    3. 失败 → getlocationinfo({errorCode:12, errorMsg:"缺少定位权限"})
//
//  ⚠️ canImport(AMapLocationKit) 守卫：SDK 未链接时 LocationService 抛 sdkNotLinked，
//  此处兜底走 stub（与 Phase 2 一致），仅响应 callback 不触发反向 getlocationinfo。
//

import Foundation
import os.log

// 工程默认 SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor，全局 let 也会被 MainActor 隔离；
// handler 闭包是 @Sendable 非隔离上下文，故显式 nonisolated（Logger 本身 Sendable，安全）。
private nonisolated let startLocLog = Logger(subsystem: "ylgamehall", category: "Location")

public enum StartLocationHandler {

    /// - Parameters:
    ///   - label: 容器标识（`lobby` / `subGame`），仅用于诊断日志。
    ///   - location: **本容器私有**的 LocationService。msext 大厅 / 子游戏各持一个
    ///     `AMapLocationManager`，不可共用（见 LocationService.init 注释）。
    public static func register(on bridge: any BridgeProtocol,
                                label: String = "?",
                                location: LocationService) {
        bridge.register("startlocation") { data, callback in
            // 与 msext 行为一致：先回 cb 字面，不等定位完成
            callback?(.string("startlocation"))

            diagLog("[\(label)] ← startlocation 入参 type=\(data?.asLooseString ?? "nil")")

            #if canImport(AMapLocationKit)
            // msext NewRootVC.m:411-421 / gameController.m:533-543 的分支逐字等价：
            //   int tempinfo = [data intValue];
            //   if (tempinfo == 1) [self.locationManager startUpdatingLocation];  // 持续
            //   else               [self reGeocodeAction];                        // 单次
            // 现网两个 H5 包（gamehall / jinxianmahjong）实测入参恒为 2，持续定位分支
            // 是死代码；但 msext 有、我们缺就是原生侧的行为不一致，按 msext 补齐。
            let isContinuous = Int(data?.asLooseString ?? "") == 1
            Task { @MainActor in
                // 单次路径下一次请求可能产出两条 outcome（msext completionBlock 的原始
                // 控制流）；持续路径下每次定位更新都会回一条 .success。
                let dispatch: @MainActor (LocationOutcome) -> Void = { outcome in
                    switch outcome {
                    case .failure:
                        // msext gameController.m:2507 等价
                        diagLog("[\(label)] → getlocationinfo errorCode=12")
                        bridge.call("getlocationinfo", data: .object([
                            "errorCode": .number(12),
                            "errorMsg":  .string("缺少定位权限")
                        ]), callback: nil)
                    case .success(let payload):
                        // msext gameController.m:2528 的 9 字段 + `errorCode: 0`。
                        //
                        // ⚠️ 契约影响（docs/H5-Native-Contract.md §3.2 表 B）：
                        // msext 全工程只在**失败**分支发 `errorCode: 12`，成功分支
                        // 9 个字段里**没有** errorCode —— 这是原工程的遗漏。
                        // H5 侧以 `errorCode == 0` 作为"定位数据有效"的判据：
                        // 子游戏 jinxianmahjong 的 05_Func.js（2026-07-19）在
                        // `Func.startlocation` / `Func.getlocation` 的兜底桩里
                        // 自造的定位对象就带 `"errorCode":0`；而大厅 gamehall 的
                        // 05_Func.js（2026-02-04）整份文件都没有 errorCode。
                        // 这正是"大厅定位正常、子游戏拿不到"的原生侧成因：成功包缺
                        // errorCode 时子游戏 H5 不认这份数据。
                        // 按项目方决定补齐（此处是**有意偏离** msext 的一处，其余
                        // 定位链路仍严格对齐）。
                        diagLog("[\(label)] → getlocationinfo 成功 city=\(payload.city) province=\(payload.province)")
                        bridge.call("getlocationinfo", data: .object([
                            "errorCode": .number(0),
                            "address":   .string(payload.address),
                            "city":      .string(payload.city),
                            "cityCode":  .string(payload.cityCode),
                            "country":   .string(payload.country),
                            "district":  .string(payload.district),
                            "latitude":  .decimal(payload.latitude),    // ← 数字（非字符串），6 位小数
                            "longitude": .decimal(payload.longitude),   // ← 数字（非字符串），6 位小数
                            "province":  .string(payload.province),     // ← 小写 p
                            "street":    .string(payload.street)
                        ]), callback: nil)
                    }
                }

                if isContinuous {
                    location.startContinuous(caller: label, emit: dispatch)
                } else {
                    location.requestOnce(caller: label, emit: dispatch)
                }
            }
            #endif
        }
    }
}
