import UIKit
import WebKit

/// **配置模式**:让客户已有 WKWebView 获得 SDK 全部能力。
///
/// **API**:
/// ```swift
/// let handle = TencentQianWebViewConfigurator.apply(
///     webView: myWebView,
///     hostController: self,
///     config: TencentQianWebViewConfig(...),
///     callbacks: SignCallbacks(onSignResult: { ... })
/// )
/// myWebView.load(URLRequest(url: url))
/// // 退出页面时:
/// handle.detach()
/// ```
///
/// **幂等性**:对同一 ``WKWebView`` 二次 apply 会替换前次 delegate,不堆叠。
/// **chained delegate**:本期暂未保留客户原 delegate;客户应在 apply 之前不要自行赋值。
public enum TencentQianWebViewConfigurator {

    @discardableResult
    public static func apply(
        webView: WKWebView,
        hostController: UIViewController? = nil,
        config: TencentQianWebViewConfig = .default,
        callbacks: SignCallbacks = SignCallbacks()
    ) -> Handle {

        let onEvent: (SdkEvent) -> Void = { event in
            DispatchQueue.main.async { callbacks.onEvent?(event) }
        }
        let onSignResult: (SignResult) -> Void = { result in
            DispatchQueue.main.async { callbacks.onSignResult?(result) }
        }

        let schemeInterceptor = SchemeInterceptor(
            onSignResult: onSignResult,
            onEvent: onEvent
        )
        let externalSchemeHandler = ExternalSchemeHandler(
            externalSchemes: config.externalSchemes,
            onEvent: onEvent
        )

        let navDelegate = QianNavigationDelegate(
            config: config,
            schemeInterceptor: schemeInterceptor,
            externalSchemeHandler: externalSchemeHandler,
            onEvent: onEvent
        )
        let uiDelegate = QianUIDelegate(onEvent: onEvent, hostingController: hostController)

        // 创建下载管理器（统一 URLSession + Cookie 同步，全版本可用）
        let downloadMgr = QianDownloadManager(onEvent: onEvent, hostController: hostController)
        navDelegate.setDownloadManager(downloadMgr)

        // 替换 delegate(幂等)
        webView.navigationDelegate = navDelegate
        webView.uiDelegate = uiDelegate

        // 绑定 webView 用于白屏自检
        navDelegate.bindWebView(webView)

        // UA 注入已由 WKConfigurationFactory.makeConfiguration 在创建 WKWebView 之前完成,
        // 此处无需额外操作(applicationNameForUserAgent 是同步设置,创建后即生效)。

        // 强引用所有 delegate(防止释放)
        let retainer = DelegateRetainer(
            navDelegate: navDelegate,
            uiDelegate: uiDelegate,
            downloadManager: downloadMgr
        )
        objc_setAssociatedObject(webView, &Handle.retainerKey, retainer, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

        return Handle(webView: webView, retainer: retainer)
    }

    /// SDK 注入句柄。
    ///
    /// - ``detach()``: 仅解绑 SDK 注入的 delegate，WKWebView 实例保留可继续使用。
    /// - ``detachAndDestroy()``: 解绑 SDK 并销毁 WKWebView，释放全部资源。
    public final class Handle {
        fileprivate static var retainerKey: UInt8 = 0

        private weak var webView: WKWebView?
        private let retainer: DelegateRetainer

        fileprivate init(webView: WKWebView, retainer: DelegateRetainer) {
            self.webView = webView
            self.retainer = retainer
        }

        /// 加载 URL。UA 已在 WKWebViewConfiguration 创建时同步注入，直接加载即可。
        public func load(_ url: URL) {
            webView?.load(URLRequest(url: url))
        }

        /// 主动触发白屏自检，适合在 host VC 重新可见时调用(viewDidAppear:)。
        public func attemptBlankRecoveryIfNeeded() {
            retainer.navDelegate.attemptBlankRecoveryIfNeeded()
        }

        /// 解绑 SDK 注入的 delegate，但**不销毁** WKWebView。
        ///
        /// 适用于客户需要复用同一个 WKWebView 实例的场景（例如解绑后加载其他非签署页面）。
        /// 调用后 WKWebView 仍可正常使用，只是不再具备 SDK 能力。
        public func detach() {
            guard let wv = webView else { return }
            wv.navigationDelegate = nil
            wv.uiDelegate = nil
            objc_setAssociatedObject(wv, &Handle.retainerKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }

        /// 解绑 SDK 并销毁 WKWebView，释放全部资源。
        ///
        /// 等价于 ``detach()`` + 标准 WKWebView 销毁流程：
        /// `stopLoading → load(about:blank) → removeFromSuperview()`。
        ///
        /// 适用于页面退出时一并释放 WKWebView 资源的场景（最常见用法）。
        /// 调用后 WKWebView 实例不可再使用。
        public func detachAndDestroy() {
            guard let wv = webView else {
                detach()
                return
            }
            detach()
            wv.stopLoading()
            // 先加载空白页清空 JS 上下文，避免 WKWebView 在释放时因残留 JS 回调产生警告
            wv.load(URLRequest(url: URL(string: "about:blank")!))
            wv.removeFromSuperview()
        }
    }

    /// 内部强引用持有 delegate / 下载管理器,防止被 ARC 释放。
    fileprivate final class DelegateRetainer {
        let navDelegate: QianNavigationDelegate
        let uiDelegate: QianUIDelegate
        let downloadManager: QianDownloadManager

        init(navDelegate: QianNavigationDelegate, uiDelegate: QianUIDelegate, downloadManager: QianDownloadManager) {
            self.navDelegate = navDelegate
            self.uiDelegate = uiDelegate
            self.downloadManager = downloadManager
        }
    }
}
