//
//  QQShare.swift
//  ylgamehall
//
//  QQ 分享 SharePlatform 实现：
//    - type != "2" → 链接分享：mqqapi://share/to_fri 全量参数（src_type/app_id
//                  /thirdAppDisplayName/callback_* + file_type=news + 双层
//                  base64 title/desc/url），对齐 msext QQShareManager
//                  shareToQQFriend:QQShareTypeNews 的 URL Scheme fallback 路径
//    - type == "2" → 截图分享：UIPasteboard + 引导框（URL Scheme 内联 base64
//                  截图字节 1080p ≈ 5MB URL，iOS openURL 长度限制下不可靠，
//                  msext 实战也常被截断；保持剪贴板 + 引导框模式）
//
//  **不依赖 QQ SDK**（CLAUDE.md ADR-006）：URL Scheme + 公开 AppID 即可调起
//  QQ 原生分享 UI。
//
//  AppID 102793577：复用 daoqi 注册的 QQ 互联 AppID。原绑 bundle id 是
//  com.chao.msext，新外壳 com.skyapp.ylgamehall —— QQ 客户端唤起分享 UI 时
//  实测不严格校验，但若分享后回调 / 反向接口被拒，需 https://connect.qq.com/
//  重新注册 AppID 绑给 com.skyapp.ylgamehall（只改下面 4 个常量 + Info.plist
//  CFBundleURLSchemes 的 tencent<新ID>）。
//
//  fire-and-forget：sharesuccess 已在 FriendsShareHandler 立即触发。
//

import UIKit
import os.log

@MainActor
public final class QQShare: SharePlatform {

    public static let shared = QQShare()

    private static let log = Logger(subsystem: "ylgamehall", category: "QQShare")

    public let name = "QQ"

    // MARK: - QQ 互联 AppID 配置（沿用 daoqi）

    /// QQ 互联开放平台 AppID（沿用 daoqi `kQQAppID`）
    private static let appID = "102793577"
    /// app_id 对应的 8 位 hex（102793577 → 0x6208169 → "06208169"，前补 0 到 8 位）。
    /// QQ 客户端要求 callback_name == "QQ" + hex(appID, padding=8)
    private static let appIDHex = "06208169"
    /// 与 Info.plist CFBundleDisplayName 字面对齐，msext kQQAppName 同源
    private static let appName = "进贤聚友棋牌"

    public var isInstalled: Bool {
        guard let url = URL(string: "mqqapi://") else { return false }
        return UIApplication.shared.canOpenURL(url)
    }

    public init() {}

    public func share(_ content: ShareContent, scene: ShareScene) {
        guard isInstalled else { return }

        if content.type == "2" {
            shareScreenshot()
        } else {
            shareLink(content, scene: scene)
        }
    }

    // MARK: - 链接分享（URL Scheme 全量参数）

    /// 对齐 msext QQShareManager.m:200-523 `shareToQQFriend:QQShareTypeNews`
    /// 的 URL Scheme fallback 路径。URL 大致结构：
    ///
    ///   mqqapi://share/to_fri?
    ///     version=1
    ///     &src_type=app
    ///     &thirdAppDisplayName=<urlenc(base64(appName))>
    ///     &app_name=<urlenc(base64(appName))>
    ///     &app_id=102793577
    ///     &share_id=102793577
    ///     &callback_type=scheme
    ///     &callback_name=QQ06208169
    ///     &file_type=news
    ///     &req_type=1
    ///     &cflag=0
    ///     &title=<urlenc(base64(title))>
    ///     &description=<urlenc(base64(desc))>
    ///     &url=<urlenc(base64(webpageUrl))>
    ///
    /// scene == .timeline → share/to_qzone（QQ 空间）；.friend → share/to_fri（QQ 好友）
    private func shareLink(_ content: ShareContent, scene: ShareScene) {
        let host = (scene == .timeline) ? "share/to_qzone" : "share/to_fri"

        // 1. 公共参数（AppID / 回调）—— 缺任意一项 QQ 都不弹分享面板
        var parts: [String] = [
            "version=1",
            "src_type=app",
            "file_type=news",
            "req_type=1",
            "cflag=0",
            "app_id=\(Self.appID)",
            "share_id=\(Self.appID)",
            "callback_type=scheme",
            "callback_name=QQ\(Self.appIDHex)"
        ]
        let encodedAppName = Self.encode(Self.base64(Self.appName))
        parts.append("thirdAppDisplayName=\(encodedAppName)")
        parts.append("app_name=\(encodedAppName)")

        // 2. 内容参数 —— News 类型 title/desc/url 必须 base64 后再 URL-encode
        if !content.title.isEmpty {
            parts.append("title=\(Self.encode(Self.base64(content.title)))")
        }
        if !content.desc.isEmpty {
            parts.append("description=\(Self.encode(Self.base64(content.desc)))")
        }
        if !content.webpageUrl.isEmpty {
            parts.append("url=\(Self.encode(Self.base64(content.webpageUrl)))")
        }

        // 3. 缩略图 —— 字段名是 previewimagedata（图像字节 base64），不是
        //    msext 写的 previewimageUrl=base64("file://..."): 后者跨 app 沙盒读不到
        //    本地文件（控制台 `unable to make sandbox extension` 即此原因），实测
        //    QQ 渲染破图占位。改对齐 QQ Open SDK 的 previewimagedata 字段，把
        //    app icon JPEG 字节本身 base64 内联到 URL。
        //    iOS openURL URL 长度限制 ~64KB，app icon 60-180px JPEG 0.6 编码后
        //    base64 ≈ 5-15KB，余量充足。
        if let thumbBase64 = Self.appIconBase64() {
            parts.append("previewimagedata=\(Self.encode(thumbBase64))")
        }

        let urlString = "mqqapi://\(host)?\(parts.joined(separator: "&"))"
        Self.log.debug("QQShare URL → \(urlString, privacy: .public)")

        guard let qqURL = URL(string: urlString),
              UIApplication.shared.canOpenURL(qqURL) else {
            Self.log.error("QQShare canOpenURL=false url=\(urlString, privacy: .public)")
            return
        }
        UIApplication.shared.open(qqURL)
    }

    // MARK: - 截图分享（剪贴板 + 引导框）

    /// 对齐 msext QQShareManager.m:1876-1906 isScreenshotShare 分支：
    /// 原项目用 QQ SDK QQShareTypeImage 直接传 UIImage；URL Scheme 路径
    /// 理论可走 `image_base64` 内联，但 1080p 截图编码后 URL 约 5MB，iOS
    /// `openURL` 不可靠。保持剪贴板 + 引导框模式（与 Douyin 截图同款）。
    private func shareScreenshot() {
        do {
            let shot = try ImageProvider.captureScreenshot()
            UIPasteboard.general.image = shot
            Self.log.debug("QQShare 截图已复制到剪贴板")
        } catch {
            Self.log.error("QQShare 截图失败 \(error.localizedDescription, privacy: .public)")
            return
        }

        let message = """
        ✅ 图片已复制到剪贴板

        🎯 分享到好友或群聊步骤：
        1️⃣ 打开 QQ，选择好友或群聊进入聊天
        2️⃣ 在输入框长按粘贴图片
        3️⃣ 点击发送即可分享
        """
        showGuidanceAlert(title: "QQ 分享准备完成", message: message)
    }

    /// 弹"立即打开 QQ / 稍后分享"二选一 Alert，与 DouyinShare 同款 UX。
    private func showGuidanceAlert(title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "立即打开 QQ", style: .default) { _ in
            if let url = URL(string: "mqqapi://"),
               UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url)
            } else if let url = URL(string: "mqq://"),
                      UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url)
            }
        })
        alert.addAction(UIAlertAction(title: "稍后分享", style: .cancel))

        guard let topVC = Self.topViewController() else { return }
        topVC.present(alert, animated: true)
    }

    private static func topViewController() -> UIViewController? {
        let scene = UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first(where: { $0.activationState == .foregroundActive })
            ?? UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first
        guard var top = scene?.windows.first(where: \.isKeyWindow)?.rootViewController
                    ?? scene?.windows.first?.rootViewController else { return nil }
        while let presented = top.presentedViewController { top = presented }
        if let nav = top as? UINavigationController, let visible = nav.visibleViewController {
            return visible
        }
        return top
    }

    // MARK: - 缩略图（app icon JPEG → base64 内联到 URL）

    /// app icon JPEG 0.6 → base64 字符串。QQ Open SDK previewimagedata 字段的值。
    /// 跨 app 沙盒下 file:// 路径不可读（控制台 `sandbox extension`），唯一可
    /// 靠的传递方式是把图像字节直接 base64 编码到 URL 参数里。
    private static func appIconBase64() -> String? {
        guard let data = appIconImage().jpegData(compressionQuality: 0.6) else { return nil }
        return data.base64EncodedString()
    }

    /// app icon UIImage，与 WechatShare.appIconImage() 同源策略
    /// （读 Info.plist CFBundleIcons → CFBundlePrimaryIcon → CFBundleIconFiles 最后一个）
    private static func appIconImage() -> UIImage {
        if let icons = Bundle.main.object(forInfoDictionaryKey: "CFBundleIcons") as? [String: Any],
           let primary = icons["CFBundlePrimaryIcon"] as? [String: Any],
           let files = primary["CFBundleIconFiles"] as? [String],
           let last = files.last,
           let icon = UIImage(named: last) {
            return icon
        }
        if let icon = UIImage(named: "AppIcon") { return icon }
        let renderer = UIGraphicsImageRenderer(size: CGSize(width: 1, height: 1))
        return renderer.image { _ in }
    }

    // MARK: - URL / Base64 编码

    /// RFC 3986 unreserved + URL-encode（与 msext line 1262-1268 等价）
    nonisolated static func encode(_ string: String) -> String {
        var allowed = CharacterSet.alphanumerics
        allowed.insert(charactersIn: "-._~")
        return string.addingPercentEncoding(withAllowedCharacters: allowed) ?? ""
    }

    /// UTF-8 → base64（与 msext base64Encode: 等价）
    nonisolated static func base64(_ string: String) -> String {
        guard let data = string.data(using: .utf8) else { return "" }
        return data.base64EncodedString()
    }
}
