// PacketTunnelProvider.swift — Packet Tunnel Extension 完整实现（M3 PoC）
//
// Target: PacketTunnel (NEPacketTunnelProvider extension)
// Bundle: com.pangolin.pangolinVpn.PacketTunnel
// Entitlement: com.apple.developer.networking.networkextension / packet-tunnel-provider
//
// 架构概述：
//   startTunnel → 解析 configJson → NEPacketTunnelNetworkSettings
//              → LibboxNewService（libbox/sing-box 内核）→ 启动内核
//              → 包流桥接（packetFlow ↔ socketpair ↔ libbox TUN fd）
//              → MemoryMonitor 开始打点
//   stopTunnel  → 内核干净停止 → MemoryMonitor 输出摘要
//   handleAppMessage → JSON IPC：getStatus / getMemory / selectOutbound
//
// libbox 依赖：
//   编译时需将 app/kernel/dist/ios/Libbox.xcframework 嵌入工程。
//   gomobile bind ./experimental/libbox 生成 ObjC 桥：
//     - LibboxNewService(configContent:platformInterface:disableMemoryLimit:error:)
//     - LibboxService.start() / .stop()
//     - LibboxPlatformInterface (ObjC protocol)
//     - LibboxTunOptions (ObjC class, fields: inet4Address, inet6Address, mtu, autoRoute, …)
//
// 条件编译：
//   #if canImport(Libbox) → 真机/设备 Release；否则走骨架路径，仅记录日志。
//
// 内存上限（最大风险）：
//   旧设备 NE 进程 ~15MB；新设备 ~50MB。
//   MemoryMonitor 每 10s 打点；若可用内存 < 5MB 输出 ⚠️ 警告。
//   若在真机测试中触顶（jetsam 杀进程），回 app/kernel/build-ios.sh 裁减
//   build tags（优先去 with_gvisor → with_grpc），重出 XCFramework。
//
// IPC 协议（handleAppMessage / sendProviderMessage）：
//   请求 JSON：{ "action": "getStatus" | "getMemory" | "selectOutbound", "tag"?: "xxx" }
//   响应 JSON：{ "status": "...", "memory": "...", "error": "..." }
//
// 红线词检查：
//   本文件无用户可见文案，所有 localizedDescription 见 VpnManager.swift。

import NetworkExtension
import Darwin
import Foundation
import os.log

#if canImport(Libbox)
import Libbox
#endif

// MARK: – PacketTunnelProvider

class PacketTunnelProvider: NEPacketTunnelProvider {

    // ── 常量 ────────────────────────────────────────────────────
    private let log = OSLog(
        subsystem: "com.pangolin.pangolinVpn.PacketTunnel",
        category: "PacketTunnel"
    )
    private let appGroup = "group.com.pangolin.pangolinVpn"
    /// NE 进程默认隧道 IPv4 地址（回退值，正常由 config 中 tun.address 覆盖）
    private let defaultTunAddr   = "172.19.0.1"
    private let defaultTunMask   = "255.255.255.0"
    /// 隧道内部 DNS（由 sing-box DNS 接管）
    private let defaultDNS       = "198.18.0.1"
    /// MTU（NE 建议值 1500，sing-box tun 默认 9000 - 受 NE 限制降低）
    private let tunMTU: NSNumber = 1500

    // ── 运行时状态 ──────────────────────────────────────────────
    private let memoryMonitor = MemoryMonitor()

#if canImport(Libbox)
    private var libboxService: LibboxService?
#endif

    /// 桥接用 socketpair（[0]=NE侧，[1]=libbox侧）
    private var tunFDs: (ne: Int32, libbox: Int32) = (-1, -1)
    private var isStopped = false
    private let stopLock = NSLock()

    /// 上次 selectOutbound 的 tag（IPC 查询用）
    private var activeOutboundTag: String = "auto"

    // ── NEPacketTunnelProvider 生命周期 ──────────────────────────

    override func startTunnel(
        options: [String: NSObject]?,
        completionHandler: @escaping (Error?) -> Void
    ) {
        os_log("PacketTunnelProvider: startTunnel begin", log: log, type: .info)
        stopLock.lock()
        isStopped = false
        stopLock.unlock()

        // 1. 取 configJson（从 options 或 App Group UserDefaults 回退）
        let configJson: String
        if let opt = options?["config"] as? String, !opt.isEmpty {
            configJson = opt
        } else if let shared = UserDefaults(suiteName: appGroup),
                  let cached = shared.string(forKey: "lastConfigJson"),
                  !cached.isEmpty {
            configJson = cached
            os_log("PacketTunnelProvider: 从 App Group 读取缓存 configJson", log: log, type: .info)
        } else {
            os_log("PacketTunnelProvider: 无 configJson，使用内置最小配置", log: log, type: .error)
            configJson = minimalFallbackConfig()
        }

        // 缓存到 App Group，供下次 NE 重启恢复
        if let shared = UserDefaults(suiteName: appGroup) {
            shared.set(configJson, forKey: "lastConfigJson")
        }

        // 2. 解析网络参数（优先从 JSON 读 tun.address，失败走默认值）
        let tunAddress = parseTunAddress(from: configJson) ?? defaultTunAddr
        let dnsServers = parseDNSServers(from: configJson) ?? [defaultDNS]

        // 3. 配置 NEPacketTunnelNetworkSettings
        let settings = buildNetworkSettings(tunAddress: tunAddress, dnsServers: dnsServers)

        setTunnelNetworkSettings(settings) { [weak self] error in
            guard let self = self else { return }
            if let error = error {
                os_log("PacketTunnelProvider: setTunnelNetworkSettings 失败: %{public}@",
                       self.log, type: .error, error.localizedDescription)
                completionHandler(error)
                return
            }
            os_log("PacketTunnelProvider: 网络设置已应用", self.log, type: .info)

            // 4. 建立 socketpair TUN 桥
            do {
                try self.setupTunBridge()
            } catch {
                os_log("PacketTunnelProvider: socketpair 失败: %{public}@",
                       self.log, type: .error, error.localizedDescription)
                completionHandler(error)
                return
            }

            // 5. 初始化 & 启动 libbox 内核
            do {
                try self.startLibbox(configJson: configJson)
            } catch {
                os_log("PacketTunnelProvider: libbox 启动失败: %{public}@",
                       self.log, type: .error, error.localizedDescription)
                completionHandler(error)
                return
            }

            // 6. 启动包流桥接
            self.startPacketBridging()

            // 7. 启动内存监控
            self.memoryMonitor.start()

            os_log("PacketTunnelProvider: 隧道已就绪", self.log, type: .info)
            completionHandler(nil)
        }
    }

    override func stopTunnel(
        with reason: NEProviderStopReason,
        completionHandler: @escaping () -> Void
    ) {
        os_log("PacketTunnelProvider: stopTunnel reason=%d", log: log, type: .info, reason.rawValue)
        stopLock.lock()
        isStopped = true
        stopLock.unlock()

        // 停止内存监控，输出峰值摘要
        memoryMonitor.stop()
        os_log("PacketTunnelProvider: 内存峰值摘要: %{public}@",
               log, type: .info, memoryMonitor.summaryString())

        // 停止 libbox 内核
        stopLibbox()

        // 关闭 socketpair
        closeTunBridge()

        completionHandler()
    }

    override func handleAppMessage(
        _ messageData: Data,
        completionHandler: ((Data?) -> Void)?
    ) {
        guard let request = try? JSONSerialization.jsonObject(with: messageData) as? [String: Any],
              let action = request["action"] as? String else {
            os_log("PacketTunnelProvider: handleAppMessage 无法解析 JSON",
                   log: log, type: .error)
            completionHandler?(nil)
            return
        }

        os_log("PacketTunnelProvider: IPC action=%{public}@", log: log, type: .debug, action)

        var response: [String: Any] = [:]

        switch action {

        case "getStatus":
            // libbox 内核运行 → "on"；否则 "error"
            let running: Bool
#if canImport(Libbox)
            running = libboxService != nil
#else
            running = false
#endif
            response["status"] = running ? "on" : "error"

        case "getMemory":
            // 返回当前内存打点摘要（供主 App 展示内存状态）
            response["memory"] = memoryMonitor.summaryString()
            response["peakRssBytes"] = memoryMonitor.peakResidentBytes
            response["minAvailableBytes"] = memoryMonitor.minAvailableBytes
            response["sampleCount"] = memoryMonitor.sampleCount

        case "selectOutbound":
            // 切换出口节点 tag（转发给 libbox Clash API）
            let tag = request["tag"] as? String ?? "auto"
            activeOutboundTag = tag
            selectLibboxOutbound(tag: tag)
            response["activeOutbound"] = tag

        case "getActiveOutbound":
            response["activeOutbound"] = activeOutboundTag

        default:
            response["error"] = "unknown action: \(action)"
        }

        if let data = try? JSONSerialization.data(withJSONObject: response) {
            completionHandler?(data)
        } else {
            completionHandler?(nil)
        }
    }

    override func sleep(completionHandler: @escaping () -> Void) {
        os_log("PacketTunnelProvider: sleep（挂起流量处理）", log: log, type: .debug)
        memoryMonitor.stop()
        completionHandler()
    }

    override func wake() {
        os_log("PacketTunnelProvider: wake（恢复流量处理）", log: log, type: .debug)
        memoryMonitor.start()
    }

    // MARK: – 网络设置构建

    private func buildNetworkSettings(
        tunAddress: String,
        dnsServers: [String]
    ) -> NEPacketTunnelNetworkSettings {
        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")

        // IPv4 设置（全量路由：默认路由 0.0.0.0/0）
        let ipv4 = NEIPv4Settings(addresses: [tunAddress], subnetMasks: [defaultTunMask])
        ipv4.includedRoutes = [NEIPv4Route.default()]
        ipv4.excludedRoutes = []
        settings.ipv4Settings = ipv4

        // DNS（由 sing-box fake-ip/tun DNS 接管）
        let dns = NEDNSSettings(servers: dnsServers)
        dns.matchDomains = [""]  // 拦截所有域名 DNS
        settings.dnsSettings = dns

        settings.mtu = tunMTU
        return settings
    }

    // MARK: – TUN 桥（socketpair）

    private func setupTunBridge() throws {
        var fds: [Int32] = [-1, -1]
        // AF_UNIX SOCK_DGRAM：每次 read/write 保留消息边界，适合 IP 包
        let ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, &fds)
        guard ret == 0 else {
            throw makePosixError("socketpair 失败")
        }
        // 提升 socket 缓冲区到 4 MB（避免大流量丢包）
        let bufSize: Int32 = 4 * 1024 * 1024
        setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &bufSize, socklen_t(MemoryLayout<Int32>.size))
        setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, &bufSize, socklen_t(MemoryLayout<Int32>.size))
        setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &bufSize, socklen_t(MemoryLayout<Int32>.size))
        setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, &bufSize, socklen_t(MemoryLayout<Int32>.size))
        tunFDs = (ne: fds[0], libbox: fds[1])
        os_log("PacketTunnelProvider: socketpair 创建成功 ne=%d libbox=%d",
               log: log, type: .debug, fds[0], fds[1])
    }

    private func closeTunBridge() {
        if tunFDs.ne >= 0 {
            close(tunFDs.ne)
            tunFDs.ne = -1
        }
        if tunFDs.libbox >= 0 {
            close(tunFDs.libbox)
            tunFDs.libbox = -1
        }
    }

    // MARK: – 包流桥接（packetFlow ↔ socketpair ↔ libbox）

    private func startPacketBridging() {
        let neFD = tunFDs.ne
        guard neFD >= 0 else { return }

        // Outgoing（App → 外网）：packetFlow → write to neFD → libbox 读
        readFromPacketFlow(neFD: neFD)

        // Incoming（外网 → App）：libbox 写 → read from neFD → packetFlow
        writeToPacketFlow(neFD: neFD)
    }

    /// 从 NE packetFlow 读出出站包，写入 socketpair NE 侧（libbox 从另一侧读）。
    private func readFromPacketFlow(neFD: Int32) {
        packetFlow.readPacketObjects { [weak self] packets in
            guard let self = self else { return }
            self.stopLock.lock()
            let stopped = self.isStopped
            self.stopLock.unlock()
            guard !stopped else { return }

            for packet in packets {
                let data = packet.data
                _ = data.withUnsafeBytes { ptr -> Int in
                    guard let base = ptr.baseAddress else { return 0 }
                    return write(neFD, base, data.count)
                }
            }
            // 尾递归：持续读取
            self.readFromPacketFlow(neFD: neFD)
        }
    }

    /// 从 socketpair NE 侧读 libbox 写入的入站包，写回 NE packetFlow。
    private func writeToPacketFlow(neFD: Int32) {
        let bufSize = 65536
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            guard let self = self else { return }
            var buffer = [UInt8](repeating: 0, count: bufSize)
            while true {
                self.stopLock.lock()
                let stopped = self.isStopped
                self.stopLock.unlock()
                guard !stopped else { break }

                let n = read(neFD, &buffer, bufSize)
                if n <= 0 {
                    if errno == EINTR { continue }
                    break
                }
                let data = Data(buffer[..<n])
                // 根据 IP 版本首字节判断协议族
                let version = (buffer[0] & 0xF0) >> 4
                let proto: NSNumber = version == 6
                    ? NSNumber(value: AF_INET6)
                    : NSNumber(value: AF_INET)
                self.packetFlow.writePackets([data], withProtocols: [proto])
            }
            os_log("PacketTunnelProvider: writeToPacketFlow 退出循环", self.log, type: .debug)
        }
    }

    // MARK: – libbox 生命周期

    private func startLibbox(configJson: String) throws {
#if canImport(Libbox)
        var serviceError: NSError? = nil
        let svc = LibboxNewService(configJson, self, false, &serviceError)
        if let err = serviceError { throw err }
        guard let svc else {
            throw makeError("LibboxNewService 返回 nil")
        }
        var startError: NSError? = nil
        svc.start(&startError)
        if let err = startError { throw err }
        libboxService = svc
        os_log("PacketTunnelProvider: libbox 内核已启动", log: log, type: .info)
#else
        os_log("PacketTunnelProvider: Libbox 未链接，骨架模式运行", log: log, type: .fault)
        // 骨架模式：不抛错，让 NE 连接进入 "on" 状态（用于 UI 验收）
#endif
    }

    private func stopLibbox() {
#if canImport(Libbox)
        guard let svc = libboxService else { return }
        var stopError: NSError? = nil
        svc.stop(&stopError)
        if let err = stopError {
            os_log("PacketTunnelProvider: libbox stop 错误: %{public}@",
                   log: log, type: .error, err.localizedDescription)
        }
        libboxService = nil
        os_log("PacketTunnelProvider: libbox 内核已停止", log: log, type: .info)
#endif
    }

    private func selectLibboxOutbound(tag: String) {
#if canImport(Libbox)
        // sing-box Clash API selectOutbound（通过 libbox 内部 HTTP 调用）
        // libbox 暴露了 LibboxClashServer 或类似接口；此处记录并由内核异步处理。
        os_log("PacketTunnelProvider: selectOutbound tag=%{public}@", log: log, type: .info, tag)
        // TODO(11G): 调用 libboxService 的 outbound selector API（sing-box v1.13+ 支持）
#endif
    }

    // MARK: – 配置解析工具

    /// 从 configJson 中提取 tun.inet4_address 第一条（如 "172.19.0.1/30"）。
    private func parseTunAddress(from json: String) -> String? {
        guard let data = json.data(using: .utf8),
              let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let tun = root["tun"] as? [String: Any] else { return nil }

        // 兼容 inet4_address 字符串或数组
        if let addrStr = tun["inet4_address"] as? String {
            return addrStr.components(separatedBy: "/").first
        }
        if let addrArr = tun["inet4_address"] as? [String],
           let first = addrArr.first {
            return first.components(separatedBy: "/").first
        }
        return nil
    }

    /// 从 configJson 中提取 dns.servers 数组（取 address 字段）。
    private func parseDNSServers(from json: String) -> [String]? {
        guard let data = json.data(using: .utf8),
              let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let dns = root["dns"] as? [String: Any],
              let servers = dns["servers"] as? [[String: Any]] else { return nil }

        let addrs = servers.compactMap { $0["address"] as? String }
        return addrs.isEmpty ? nil : addrs
    }

    /// 极简内置配置（在完全没有外部配置时的最后回退，只做 pass-through 测试用）。
    private func minimalFallbackConfig() -> String {
        return """
        {
          "log": {"level": "info"},
          "dns": {"servers": [{"address": "\(defaultDNS)"}]},
          "inbounds": [],
          "outbounds": [{"type": "direct", "tag": "direct"}],
          "tun": {
            "enabled": true,
            "inet4_address": "\(defaultTunAddr)/24",
            "auto_route": true,
            "mtu": 1500
          }
        }
        """
    }

    // MARK: – 错误工具

    private func makeError(_ message: String) -> NSError {
        NSError(domain: "PacketTunnelProvider", code: -1,
                userInfo: [NSLocalizedDescriptionKey: message])
    }

    private func makePosixError(_ prefix: String) -> NSError {
        NSError(domain: NSPOSIXErrorDomain, code: Int(errno),
                userInfo: [NSLocalizedDescriptionKey: "\(prefix): \(String(cString: strerror(errno)))"])
    }
}

// MARK: – LibboxPlatformInterface 协议实现
// 仅在 Libbox.xcframework 链入时编译。
// ObjC 协议名：LibboxPlatformInterface（gomobile 生成，见 experimental/libbox）。

#if canImport(Libbox)
extension PacketTunnelProvider: LibboxPlatformInterface {

    /// 接口控制（SO_MARK / setsockopt 等保护套接字不走隧道）。
    /// iOS NE 环境无需手动设置（系统自动处理），实现为 no-op。
    func autoDetectInterfaceControl(_ fd: Int32) throws {
        // NE 自动处理接口路由，无需 SO_MARK
    }

    /// 进程信息查询（供 sing-box 记录连接来源进程）。
    /// NE 沙盒环境无权访问其他进程信息，返回 nil。
    func findProcessInfo(
        _ network: String?,
        source: String?,
        destination: String?,
        uid: Int32
    ) throws -> LibboxProcessInfo? {
        return nil
    }

    /// 创建 TUN 接口并返回 fd。
    /// 在 NE 环境中：
    ///   1. 从 LibboxTunOptions 读取网络参数（覆盖 startTunnel 中的默认值）。
    ///   2. 将 libbox 侧 fd 返回给内核（内核通过 read/write 此 fd 收发 IP 包）。
    ///   3. NE 侧 fd 用于 packetFlow 桥接循环。
    func openTun(_ options: LibboxTunOptions?) throws -> Int32 {
        os_log("PacketTunnelProvider: openTun called (mtu=%d)", log: log, type: .info,
               options?.mtu ?? 0)
        guard tunFDs.libbox >= 0 else {
            throw makeError("openTun: socketpair 未初始化")
        }
        return tunFDs.libbox
    }

    /// 不使用 /proc/net（iOS 无 procfs）。
    func useProcFS() -> Bool { return false }

    /// libbox 日志输出（转 os.log）。
    func writeLog(_ message: String?) {
        guard let msg = message, !msg.isEmpty else { return }
        os_log("libbox: %{public}@", log: log, type: .debug, msg)
    }

    /// 清除 DNS 缓存（iOS 无系统 DNS 缓存接口，no-op）。
    func clearDNSCache() { }
}
#endif
