// VpnChannel.swift — 主 app 侧 VPN 控制(方案B / P1 骨架)
//
// 把 Flutter 的 pangolin/vpn(MethodChannel)+ pangolin/vpn/status、pangolin/vpn/stats
// (EventChannel)接到 NETunnelProviderManager:安装/启停 PacketTunnel System Extension、
// 回传状态与速率。契约对齐 client/lib/bridge/vpn_bridge.dart 的 VpnNativeBridge。
//
// 注册:在 MainFlutterWindow.awakeFromNib 里 `VpnChannel.register(with: flutterViewController)`。
//
// ⚠️ 站外分发首启需先**激活 System Extension**(OSSystemExtensionRequest,见
//    activateSystemExtensionIfNeeded + 安装文档步骤 4),用户会在「系统设置→隐私与安全性」
//    点允许;激活成功后 NETunnelProviderManager 才能加载该 provider。

import FlutterMacOS
import NetworkExtension
import SystemExtensions
import os.log

// os_log + %{public} —— 让日志在 Console.app / `log show` 里可见(NSLog 的 %@ 参数会被
// 系统 redact 成 <private>,排障时看不到内容)。os_log(C API)自 macOS 10.12 起可用,
// 兼容 Runner 的 10.15 部署目标。过滤:`log show --predicate 'subsystem == "com.pangolin.pangolin"'`。
private let vpnLogObj = OSLog(subsystem: "com.pangolin.pangolin", category: "vpn")
private func vpnLog(_ message: String) {
    os_log("%{public}@", log: vpnLogObj, type: .default, message)
    NSLog("[pangolin/vpn] %@", message)  // 同时进 stderr,flutter run 控制台也能看到
}

final class VpnChannel: NSObject {
    private static let tunnelBundleId = "com.pangolin.pangolin.PacketTunnel"

    private var statusSink: FlutterEventSink?
    private var statsSink: FlutterEventSink?
    private var statusObserver: NSObjectProtocol?
    private var statsTimer: Timer?
    private var manager: NETunnelProviderManager?
    private var sysextDelegate: SysExtActivationDelegate?

    static func register(with registrar: FlutterPluginRegistrar) {
        let instance = VpnChannel()
        let method = FlutterMethodChannel(name: "pangolin/vpn",
                                          binaryMessenger: registrar.messenger)
        method.setMethodCallHandler(instance.handle)

        FlutterEventChannel(name: "pangolin/vpn/status", binaryMessenger: registrar.messenger)
            .setStreamHandler(StatusStreamHandler(owner: instance))
        FlutterEventChannel(name: "pangolin/vpn/stats", binaryMessenger: registrar.messenger)
            .setStreamHandler(StatsStreamHandler(owner: instance))

        instance.observeStatus()
    }

    // ── MethodChannel ───────────────────────────────────────────────
    private func handle(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
        switch call.method {
        case "start":
            guard let configJson = call.arguments as? String else {
                result(FlutterError(code: "bad_args", message: "expected config json", details: nil))
                return
            }
            Task { await self.start(configJson, result) }
        case "stop":
            Task { await self.stop(result) }
        case "getStatus":
            result(Self.statusString(manager?.connection.status ?? .invalid))
        case "selectOutbound":
            // TODO: 经 sendProviderMessage / libbox CommandClient 切 outbound。
            result(nil)
        case "getActiveOutbound":
            result("auto")
        case "setKillSwitch":
            // includeAllNetworks / on-demand 实现 kill switch(后续)。
            result(nil)
        default:
            result(FlutterMethodNotImplemented)
        }
    }

    private func start(_ configJson: String, _ result: @escaping FlutterResult) async {
        vpnLog("start() 收到调用, config 长度=\(configJson.count) bytes")
        do {
            vpnLog("step① 激活 System Extension …")
            try await activateSystemExtensionIfNeeded()
            vpnLog("step① System Extension 激活完成 ✓")

            vpnLog("step② 装配 NETunnelProviderManager …")
            let mgr = try await loadOrCreateManager()
            self.manager = mgr
            vpnLog("step② manager 就绪, 当前隧道状态=\(Self.statusString(mgr.connection.status))")

            vpnLog("step③ startVPNTunnel(options: configContent) …")
            try mgr.connection.startVPNTunnel(options: [
                "configContent": configJson as NSString,
            ])
            vpnLog("step③ startVPNTunnel 调用已返回(实际起停由 NEVPNStatus 流驱动)✓")
            result(nil)
        } catch {
            let ns = error as NSError
            vpnLog("start FAILED ✗ domain=\(ns.domain) code=\(ns.code) desc=\(ns.localizedDescription) userInfo=\(ns.userInfo)")
            result(FlutterError(code: "start_failed", message: error.localizedDescription, details: nil))
        }
    }

    private func stop(_ result: @escaping FlutterResult) async {
        manager?.connection.stopVPNTunnel()
        result(nil)
    }

    // ── NETunnelProviderManager 装配 ────────────────────────────────
    private func loadOrCreateManager() async throws -> NETunnelProviderManager {
        let all = try await NETunnelProviderManager.loadAllFromPreferences()
        vpnLog("  loadAllFromPreferences: 已有 \(all.count) 个 VPN 配置")
        let mgr = all.first ?? NETunnelProviderManager()
        let proto = (mgr.protocolConfiguration as? NETunnelProviderProtocol) ?? NETunnelProviderProtocol()
        proto.providerBundleIdentifier = Self.tunnelBundleId
        proto.serverAddress = "Pangolin"          // 仅展示用
        mgr.protocolConfiguration = proto
        mgr.localizedDescription = "Pangolin"
        mgr.isEnabled = true
        vpnLog("  saveToPreferences(providerBundleId=\(Self.tunnelBundleId)) …")
        try await mgr.saveToPreferences()
        try await mgr.loadFromPreferences()       // 保存后重载,拿到有效 connection
        vpnLog("  manager 保存+重载完成 ✓")
        return mgr
    }

    // 请求系统加载/更新 PacketTunnel System Extension。首启系统会弹「隐私与安全性」
    // 让用户允许;允许后 didFinishWithResult 回来。已是最新则快速完成。
    private func activateSystemExtensionIfNeeded() async throws {
        vpnLog("  提交 OSSystemExtensionRequest.activationRequest(id=\(Self.tunnelBundleId)) …")
        vpnLog("  主 bundle=\(Bundle.main.bundlePath)")
        let sysextDir = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/SystemExtensions").path
        vpnLog("  SystemExtensions 目录=\(sysextDir) 内容=\((try? FileManager.default.contentsOfDirectory(atPath: sysextDir)) ?? ["<读取失败>"])")
        try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Void, Error>) in
            let req = OSSystemExtensionRequest.activationRequest(
                forExtensionWithIdentifier: Self.tunnelBundleId, queue: .main)
            let delegate = SysExtActivationDelegate(continuation: cont)
            self.sysextDelegate = delegate  // 保活到回调结束
            req.delegate = delegate
            OSSystemExtensionManager.shared.submitRequest(req)
            vpnLog("  submitRequest 已提交, 等待 sysextd 回调(didFinish / didFail / needsApproval)…")
        }
    }

    // ── 状态 / 速率回传 ─────────────────────────────────────────────
    private func observeStatus() {
        statusObserver = NotificationCenter.default.addObserver(
            forName: .NEVPNStatusDidChange, object: nil, queue: .main
        ) { [weak self] note in
            guard let conn = note.object as? NEVPNConnection else { return }
            let s = Self.statusString(conn.status)
            vpnLog("NEVPNStatus 变化 → \(s) (raw=\(conn.status.rawValue))")
            self?.statusSink?(s)
        }
    }

    fileprivate func onStatusListen(_ sink: @escaping FlutterEventSink) {
        statusSink = sink
        sink(Self.statusString(manager?.connection.status ?? .invalid))
    }
    fileprivate func onStatusCancel() { statusSink = nil }

    fileprivate func onStatsListen(_ sink: @escaping FlutterEventSink) {
        statsSink = sink
        // TODO(P1): 接 libbox CommandClient 订阅实时速率;骨架先每秒推占位 0。
        statsTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
            self?.statsSink?(["up": 0, "down": 0, "uplinkTotal": 0, "downlinkTotal": 0])
        }
    }
    fileprivate func onStatsCancel() { statsTimer?.invalidate(); statsTimer = nil; statsSink = nil }

    private static func statusString(_ s: NEVPNStatus) -> String {
        switch s {
        case .connected: return "on"
        case .connecting, .reasserting: return "connecting"
        case .disconnecting: return "disconnecting"
        case .disconnected, .invalid: return "off"
        @unknown default: return "error"
        }
    }
}

// EventChannel 流处理器。
private final class StatusStreamHandler: NSObject, FlutterStreamHandler {
    weak var owner: VpnChannel?
    init(owner: VpnChannel) { self.owner = owner }
    func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        owner?.onStatusListen(events); return nil
    }
    func onCancel(withArguments _: Any?) -> FlutterError? { owner?.onStatusCancel(); return nil }
}

private final class StatsStreamHandler: NSObject, FlutterStreamHandler {
    weak var owner: VpnChannel?
    init(owner: VpnChannel) { self.owner = owner }
    func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        owner?.onStatsListen(events); return nil
    }
    func onCancel(withArguments _: Any?) -> FlutterError? { owner?.onStatsCancel(); return nil }
}

// System Extension 激活请求回调。首启需用户在「系统设置→隐私与安全性」允许,
// 允许后收到 didFinishWithResult(故 await 会阻塞到用户批准 —— 这是对的,
// 扩展没加载前隧道起不来)。
private final class SysExtActivationDelegate: NSObject, OSSystemExtensionRequestDelegate {
    private let continuation: CheckedContinuation<Void, Error>
    private var resumed = false
    init(continuation: CheckedContinuation<Void, Error>) { self.continuation = continuation }

    func request(_ request: OSSystemExtensionRequest,
                 didFinishWithResult result: OSSystemExtensionRequest.Result) {
        vpnLog("sysext didFinishWithResult ✓ result=\(result.rawValue) (0=completed, 1=willCompleteAfterReboot)")
        guard !resumed else { return }
        resumed = true
        continuation.resume()
    }
    func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
        let ns = error as NSError
        // OSSystemExtensionErrorDomain code 速查:1 unknown,2 missingEntitlement,
        // 3 unsupportedParentBundleLocation,4 extensionNotFound,8 codeSignatureInvalid,
        // 9 validationFailed,10 forbiddenBySystemPolicy,13 authorizationRequired。
        vpnLog("sysext didFailWithError ✗ domain=\(ns.domain) code=\(ns.code) desc=\(ns.localizedDescription)")
        guard !resumed else { return }
        resumed = true
        continuation.resume(throwing: error)
    }
    func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
        vpnLog("sysext requestNeedsUserApproval —— 需在 系统设置 → 隐私与安全性 点「允许」(等待中…)")
    }
    func request(_ request: OSSystemExtensionRequest,
                 actionForReplacingExtension existing: OSSystemExtensionProperties,
                 withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
        vpnLog("sysext 替换扩展: 已装 v\(existing.bundleVersion)/\(existing.bundleShortVersion) → 新 v\(ext.bundleVersion)/\(ext.bundleShortVersion), 选择 replace")
        return .replace
    }
}
