//
//  BridgeBus.swift
//  ylgamehall
//
//  BridgeProtocol 的具体实现 + WKScriptMessageHandler 适配。
//  详见 docs/H5-Native-Implementation-Design.md §3.3。
//

import Foundation
import WebKit

/// H5 ↔ Native 消息总线。
///
/// **运行时约束**：必须挂在 WKWebView 上、@MainActor 单线程访问。
/// **生命周期**：构造时自动向 `WKUserContentController` 注册 `WVJBHandler` 通道，
///   销毁时由 WebView 链路自然释放（无需手动 remove）。
@MainActor
public final class BridgeBus: NSObject, BridgeProtocol, WKScriptMessageHandler {

    /// JS → Native 的 WKScriptMessageHandler 通道名。
    /// JS 端通过 `window.webkit.messageHandlers.WVJBHandler.postMessage({...})` 发送。
    public static let scriptMessageHandlerName = "WVJBHandler"

    /// JS 端注入的全局桥对象名（约定 `window.WebViewJavascriptBridge`）。
    public static let jsBridgeName = "WebViewJavascriptBridge"

    private weak var webView: WKWebView?
    private var handlers: [String: BridgeHandler] = [:]
    private var pendingCallbacks: [String: BridgeCallback] = [:]
    private var nativeCallbackCounter: UInt64 = 0

    /// 构造时绑定 WebView + 用户内容控制器，自动注册 `WVJBHandler` 消息处理器。
    public init(webView: WKWebView, controller: WKUserContentController) {
        self.webView = webView
        super.init()
        controller.add(self, name: Self.scriptMessageHandlerName)
    }

    // MARK: - BridgeProtocol

    public func register(_ name: String, handler: @escaping BridgeHandler) {
        handlers[name] = handler
    }

    public func call(_ name: String, data: BridgeData?, callback: BridgeCallback?) {
        var payload: [String: Any] = ["handlerName": name]
        if let data {
            payload["data"] = data.jsonObject
        }
        if let callback {
            nativeCallbackCounter += 1
            let cbId = "objc_cb_\(nativeCallbackCounter)"
            pendingCallbacks[cbId] = callback
            payload["callbackId"] = cbId
        }
        sendToJS(payload: payload)
    }

    // MARK: - WKScriptMessageHandler

    public func userContentController(_ userContentController: WKUserContentController,
                                       didReceive message: WKScriptMessage) {
        // body 可能是单条 dict 或一批 dict array（WVJB 支持批处理）
        if let batch = message.body as? [[String: Any]] {
            for msg in batch { handleIncoming(msg) }
        } else if let single = message.body as? [String: Any] {
            handleIncoming(single)
        } else {
            print("[BridgeBus] unrecognized message body type: \(type(of: message.body))")
        }
    }

    // MARK: - 私有

    private func handleIncoming(_ msg: [String: Any]) {
        // 优先判 responseId（JS 响应 Native 早前的 callHandler）
        if let responseId = msg["responseId"] as? String {
            let respData = msg["responseData"].flatMap { BridgeData(jsonObject: $0) }
            if let cb = pendingCallbacks.removeValue(forKey: responseId) {
                cb(respData)
            } else {
                print("[BridgeBus] stale responseId: \(responseId)")
            }
            return
        }

        // 否则是 JS → Native handler 调用
        guard let name = msg["handlerName"] as? String else {
            print("[BridgeBus] message missing handlerName: \(msg)")
            return
        }
        let data = msg["data"].flatMap { BridgeData(jsonObject: $0) }
        let callbackId = msg["callbackId"] as? String

        // 构造一个回调闭包，handler 调用即把 responseData 异步派回 JS
        let responseCallback: BridgeCallback? = makeResponseCallback(for: callbackId)

        guard let handler = handlers[name] else {
            print("[BridgeBus] no handler registered for '\(name)'")
            responseCallback?(nil)
            return
        }

        // handler 是 async，独立 Task 内 await；本方法保持 sync 返回
        Task {
            await handler(data, responseCallback)
        }
    }

    private func makeResponseCallback(for callbackId: String?) -> BridgeCallback? {
        guard let id = callbackId else { return nil }
        return { [weak self] respData in
            Task { @MainActor in
                self?.sendResponseToJS(responseId: id, data: respData)
            }
        }
    }

    private func sendResponseToJS(responseId: String, data: BridgeData?) {
        var payload: [String: Any] = ["responseId": responseId]
        if let data {
            payload["responseData"] = data.jsonObject
        }
        sendToJS(payload: payload)
    }

    private func sendToJS(payload: [String: Any]) {
        guard let webView else { return }
        guard let data = try? JSONSerialization.data(withJSONObject: payload),
              let json = String(data: data, encoding: .utf8)
        else {
            print("[BridgeBus] JSON encode failed for payload: \(payload)")
            return
        }
        // base64 包装避免 JSON 内单引号 / 反斜杠扰乱 JS 字符串字面量
        let base64 = Data(json.utf8).base64EncodedString()
        let js = "if (window.\(Self.jsBridgeName)) window.\(Self.jsBridgeName)._handleMessageFromObjC('\(base64)');"
        webView.evaluateJavaScript(js, completionHandler: nil)
    }
}
