//
//  WeChatManager.swift
//  ylgamehall
//
//  微信 SDK 持久 delegate + authorize + share 异步包装。
//  把 WXApiDelegate 的回调 → async/await，state UUID 配对多发起方。
//
//  Design §8.5；msext WXApiManager.m 等价。
//

import Foundation
import UIKit

// 微信 SDK 1.8.2（与 daoqi msext 同款）通过 Bridging Header 导入，无需 import 语句

@MainActor
public final class WeChatManager: NSObject {

    public static let shared = WeChatManager()

    /// 授权 scope（沿用 msext SGDefineInfo.h:104）
    public static let authScope = "snsapi_message,snsapi_userinfo,snsapi_friend,snsapi_contact"

    /// 待 resolve 的 authorize continuation（state UUID 配对）
    private var authPending: [String: CheckedContinuation<WXAuthCodePayload, Error>] = [:]
    /// 待 resolve 的 share continuation（FIFO 串行 — 微信回包不带配对 ID）
    private var sharePending: [CheckedContinuation<Void, Error>] = []

    public override init() {
        super.init()
    }

    // MARK: - Authorize

    /// 拉起微信授权（msext NewRootVC.m → WXApiRequestHandler.m 同款路径）。
    /// 必须用新版 SDK 2.x 的 `sendAuthReq:viewController:delegate:completion:`：
    /// - viewController = SDK 拉起微信回来时的 present 上下文（缺失则 SDK 无法弹起）
    /// - delegate = SDK 弱引用，授权完成由 WXApiDelegate.onResp 回调（与 SceneDelegate
    ///   openURL 路径解耦，授权回包独立由 SDK 直接派给 delegate）
    /// 旧版 1.x 的 `WXApi.send(req)` 已 deprecated，不传 vc/delegate 会导致 H5 调了无反应。
    public func authorize() async throws -> WXAuthCodePayload {
        let state = UUID().uuidString
        return try await withCheckedThrowingContinuation { cont in
            authPending[state] = cont
            let req = SendAuthReq()
            req.scope = Self.authScope
            req.state = state

            guard let vc = Self.topmostViewController() else {
                authPending.removeValue(forKey: state)
                cont.resume(throwing: WeChatError.authFailed(-3))
                return
            }
            // 1.x 的 sendAuthReq 返回 BOOL（同步知道是否成功拉起），无 completion
            let success = WXApi.sendAuthReq(req, viewController: vc, delegate: self)
            if !success {
                if let pending = authPending.removeValue(forKey: state) {
                    pending.resume(throwing: WeChatError.authFailed(-3))
                }
            }
            // success=true 时等 WXApiDelegate.onResp 触发（SceneDelegate openURL 链路）
        }
    }

    /// 取当前最顶层 ViewController（含 presented / nav.visible），供微信 SDK 作为
    /// modal present 的上下文。msext 直接 self（gameController/NewRootVC），新外壳
    /// AccreditLoginHandler 是 stateless enum，由本方法 resolve。
    private static func topmostViewController() -> UIViewController? {
        let windowScene = UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first(where: { $0.activationState == .foregroundActive })
            ?? UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first
        guard let root = windowScene?.windows.first(where: \.isKeyWindow)?.rootViewController
                      ?? windowScene?.windows.first?.rootViewController else {
            return nil
        }
        var top: UIViewController = root
        while let presented = top.presentedViewController {
            top = presented
        }
        if let nav = top as? UINavigationController, let visible = nav.visibleViewController {
            return visible
        }
        return top
    }

    // MARK: - Share

    public enum ShareScene {
        case session   // 微信好友
        case timeline  // 朋友圈
    }

    public struct ShareLink: Sendable {
        public let url:   String
        public let title: String
        public let desc:  String
        public let thumbnailURL: String?

        public init(url: String, title: String, desc: String, thumbnailURL: String?) {
            self.url = url; self.title = title; self.desc = desc; self.thumbnailURL = thumbnailURL
        }
    }

    /// 图片分享载荷（type=2 截图 / type=3 远端图）。
    /// imageData 是大图原始字节（JPEG 等），thumb 是缩略图源 — SDK
    /// `setThumbImage:` 内部把 thumb 压到 32KB。
    public struct ShareImage {
        public let imageData: Data
        public let thumb:     UIImage

        public init(imageData: Data, thumb: UIImage) {
            self.imageData = imageData
            self.thumb = thumb
        }
    }

    /// 链接分享（FIFO 串行）。微信回包不携带配对 ID，连续调时按入队顺序消费。
    public func shareLink(_ link: ShareLink, scene: ShareScene) async throws {
        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
            sharePending.append(cont)

            let media = WXWebpageObject()
            media.webpageUrl = link.url

            let message = WXMediaMessage()
            message.title = link.title
            message.description = link.desc
            message.mediaObject = media

            let req = SendMessageToWXReq()
            req.bText = false
            req.message = message
            switch scene {
            case .session:  req.scene = Int32(WXSceneSession.rawValue)
            case .timeline: req.scene = Int32(WXSceneTimeline.rawValue)
            }
            // 1.x: sendReq BOOL 返回；分享回包通过 SceneDelegate.openURLContexts →
            // WXApi.handleOpen(url:delegate:WeChatManager.shared) → onResp 路径到达。
            // ObjC selector +sendReq:，Swift 简化为 send(_:)（去 Req 后缀）。
            // 运行时仍调 +[WXApi sendReq:]
            WXApi.send(req)
        }
    }

    /// 图片分享（FIFO 串行，复用 sharePending）。msext gameController.m:637-678
    /// type=2 截图 / type=其它 远端图 共用同一条 WXImageObject + setThumbImage 路径。
    public func shareImage(_ img: ShareImage, scene: ShareScene) async throws {
        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
            sharePending.append(cont)

            let media = WXImageObject()
            media.imageData = img.imageData

            let message = WXMediaMessage()
            // 图片分享不需要 title / description（msext 字面：传 nil；这里传空串等价）
            message.mediaObject = media
            message.setThumbImage(img.thumb)

            let req = SendMessageToWXReq()
            req.bText = false
            req.message = message
            switch scene {
            case .session:  req.scene = Int32(WXSceneSession.rawValue)
            case .timeline: req.scene = Int32(WXSceneTimeline.rawValue)
            }
            // ObjC selector +sendReq:，Swift 简化为 send(_:)（去 Req 后缀）。
            // 运行时仍调 +[WXApi sendReq:]
            WXApi.send(req)
        }
    }

    // MARK: - Internal: resolve callbacks

    fileprivate func resolveAuth(state: String, code: String?, errCode: Int) {
        guard let cont = authPending.removeValue(forKey: state) else { return }
        if errCode == 0, let code {
            cont.resume(returning: WXAuthCodePayload(code: code, state: state))
        } else {
            cont.resume(throwing: WeChatError.authFailed(errCode))
        }
    }

    fileprivate func resolveShare(errCode: Int) {
        guard !sharePending.isEmpty else { return }
        let cont = sharePending.removeFirst()
        if errCode == 0 {
            cont.resume(returning: ())
        } else {
            cont.resume(throwing: WeChatError.shareFailed(errCode))
        }
    }
}

// MARK: - WXApiDelegate

extension WeChatManager: WXApiDelegate {

    public nonisolated func onReq(_ req: BaseReq) {
        // 我们当前只发起请求，不接受外部 req（微信 → 本 App 的 req 用于公众号回复等场景）
    }

    public nonisolated func onResp(_ resp: BaseResp) {
        // 主线程化：BaseResp 由 SDK 在主线程派发，但保留 MainActor.run 兜底
        let errCode = Int(resp.errCode)
        if let auth = resp as? SendAuthResp {
            let code = auth.code
            let state = auth.state ?? ""
            Task { @MainActor in
                Self.shared.resolveAuth(state: state, code: code, errCode: errCode)
            }
        } else if resp is SendMessageToWXResp {
            Task { @MainActor in
                Self.shared.resolveShare(errCode: errCode)
            }
        }
    }
}

// MARK: - Public payload / error types

public struct WXAuthCodePayload: Sendable {
    public let code:  String
    public let state: String
}

public enum WeChatError: Error, Sendable {
    case sdkNotLinked
    case authFailed(Int)
    case shareFailed(Int)
}
