//
//  OverlayViewController.swift
//  ylgamehall
//
//  弹层（msext threeView 等价）：WKWebView 加载外部 HTTP 页面，
//  H5 通过 `window.settings.{browser,finishweb,backgameData}` 三接口与原生交互。
//
//  契约：docs/H5-Native-Contract.md §3.4 弹层桥
//  Design：§3.4.2 / §2.4.3 栈深 ≤ 3
//
//  与 msext threeView.m 的差异：
//    - msext 用 UIWebView + JSExport (Bridgetwo)；新外壳用 WKWebView + WKScriptMessageHandler，
//      在 .atDocumentEnd 注入 `window.settings = {...}` polyfill 桥到 messageHandlers
//    - msext orientation=1 时 CGAffineTransformMakeRotation(±π/2) 旋转 webView；
//      本项目 landscape-locked，orientation 字段保留但不主动旋转（如有 H5 反馈需要再补）
//    - msext 用全局 sharedHTTPCookieStorage；本项目用 WKWebsiteDataStore.nonPersistent()
//      隔离弹层 Cookie / 缓存，避免污染主业务态（Design §3.4.2 风险条目）
//

import UIKit
import WebKit

public final class OverlayViewController: UIViewController {

    // MARK: - 入参

    private let request: OverlayRequest

    // MARK: - UI

    private var webView: WKWebView!

    /// WKScriptMessageHandler 名字（与 H5 端 polyfill 内 `webkit.messageHandlers.<name>` 一致）。
    /// 弹层 WKWebView 与大厅 WVJB 在不同 channel，可以与 WVJB 同名 handler 共存不冲突。
    /// fileprivate 让同文件下的 ScriptMessageProxy 可用 `OverlayViewController.MessageName(rawValue:)`
    fileprivate enum MessageName: String, CaseIterable {
        case browser
        case finishweb
        case backgameData
    }

    // MARK: - Init

    public init(request: OverlayRequest) {
        self.request = request
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("OverlayViewController requires OverlayRequest")
    }

    // MARK: - Lifecycle

    public override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        title = request.title

        setupWebView()
        loadRequest()
    }

    public override var supportedInterfaceOrientations: UIInterfaceOrientationMask { .landscape }
    public override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { .landscapeRight }
    public override var prefersStatusBarHidden: Bool { false }

    // MARK: - Setup

    private func setupWebView() {
        let config = WKWebViewConfiguration()
        // 与大厅 / 子游戏共用默认 websiteDataStore，cookie / localStorage 全局共享。
        // 与 msext threeView 行为对齐（UIWebView 时代无法隔离，外链页可读到游戏会话）。
        // 业务上：运营公告 / 充值 / 客服等外链页依赖大厅登录态，必须共享。
        config.websiteDataStore = .default()
        config.defaultWebpagePreferences.allowsContentJavaScript = true
        config.preferences.javaScriptCanOpenWindowsAutomatically = false
        config.allowsInlineMediaPlayback = true

        // .atDocumentEnd 注入 polyfill：与 msext threeView.webViewDidFinishLoad 等价时机
        // 用 documentEnd 而不是 documentStart，是为了让外链页自带的 JS 框架先初始化，
        // 再覆盖 / 创建 window.settings — msext 在 didFinishLoad 才注入，行为等价。
        let polyfill = MessageName.allCases.map { name in
            switch name {
            case .browser:
                return "browser:function(u){webkit.messageHandlers.browser.postMessage(String(u||''));}"
            case .finishweb:
                return "finishweb:function(){webkit.messageHandlers.finishweb.postMessage('');}"
            case .backgameData:
                return "backgameData:function(d){webkit.messageHandlers.backgameData.postMessage(String(d==null?'':d));}"
            }
        }.joined(separator: ",")
        let source = "window.settings=window.settings||{};Object.assign(window.settings,{\(polyfill)});"
        let userScript = WKUserScript(
            source: source,
            injectionTime: .atDocumentEnd,
            forMainFrameOnly: true
        )
        config.userContentController.addUserScript(userScript)

        // 3 个 MessageHandler 名字与 polyfill 内的 messageHandlers.<name> 一致
        for name in MessageName.allCases {
            config.userContentController.add(
                ScriptMessageProxy(target: self),
                name: name.rawValue
            )
        }

        webView = WKWebView(frame: .zero, configuration: config)
        #if DEBUG
        if #available(iOS 16.4, *) {
            webView.isInspectable = true
        }
        #endif
        webView.scrollView.bounces = false
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(webView)

        NSLayoutConstraint.activate([
            webView.topAnchor.constraint(equalTo: view.topAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }

    private func loadRequest() {
        guard let url = URL(string: request.url) else {
            showFatalAlert(message: "URL 无效：\(request.url)")
            return
        }
        webView.load(URLRequest(url: url))
    }

    // MARK: - Message handling

    fileprivate func handleMessage(_ name: MessageName, body: Any) {
        switch name {
        case .browser:
            // 系统 Safari 打开（msext threeView.browser: 等价）
            let raw = (body as? String) ?? ""
            guard !raw.isEmpty,
                  let target = URL(string: raw)
                            ?? raw.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
                                  .flatMap(URL.init(string:))
            else { return }
            Task { @MainActor in
                _ = await UIApplication.shared.open(target)
            }

        case .finishweb:
            // 仅 pop，不带数据（settings.finishweb()）
            AppCoordinator.shared.popOverlay(returningData: nil)

        case .backgameData:
            // pop + 发 .subGameDidReturn，父层（大厅 / 子游戏）观察后回调 H5 getWebdata
            let payload = serialize(body)
            AppCoordinator.shared.popOverlay(returningData: payload)
        }
    }

    /// 兼容 string / dict / array：dict/array 走 JSON 序列化透传给父层 H5
    private func serialize(_ body: Any) -> String {
        if let s = body as? String { return s }
        if JSONSerialization.isValidJSONObject(body),
           let bytes = try? JSONSerialization.data(withJSONObject: body, options: [.fragmentsAllowed]),
           let s = String(data: bytes, encoding: .utf8) {
            return s
        }
        return ""
    }

    private func showFatalAlert(message: String) {
        let alert = UIAlertController(
            title: "加载失败",
            message: message,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "返回", style: .default) { [weak self] _ in
            self?.navigationController?.popViewController(animated: true)
        })
        present(alert, animated: true)
    }
}

// MARK: - WKScriptMessageHandler proxy
//
// WKScriptMessageHandler 协议默认 retain 持有者，会跟 WebView 互相强引用导致 VC 不释放。
// 用一个 weak 持有 target 的 proxy 切断循环引用 — msext 时代手动 MRC release 等价做法。

private final class ScriptMessageProxy: NSObject, WKScriptMessageHandler {
    private weak var target: OverlayViewController?

    init(target: OverlayViewController) {
        self.target = target
        super.init()
    }

    func userContentController(_ userContentController: WKUserContentController,
                                didReceive message: WKScriptMessage) {
        guard let target,
              let name = OverlayViewController.MessageName(rawValue: message.name)
        else { return }
        target.handleMessage(name, body: message.body)
    }
}
