//
//  SDLTunNetworkManager.swift
//  Tun
//
//  Created by Codex on 2026/5/20.
//

import Foundation
import NetworkExtension

final class SDLTunNetworkManager: @unchecked Sendable {
    struct Settings: Sendable {
        let ipAddress: String
        let maskAddress: String
        let netAddress: String
        let networkDomain: String
        let shouldRouteDefault: Bool

        init(config: SDLConfiguration) {
            let networkAddress = config.networkAddress
            self.ipAddress = networkAddress.ipAddress
            self.maskAddress = networkAddress.maskAddress
            self.netAddress = networkAddress.netAddress
            self.networkDomain = networkAddress.networkDomain
            self.shouldRouteDefault = config.exitNode != nil
        }
    }

    private let provider: NEPacketTunnelProvider

    init(provider: NEPacketTunnelProvider) {
        self.provider = provider
    }

    func apply(settings: Settings, dnsServer: String) async throws {
        let networkSettings = self.makeNetworkSettings(settings: settings, dnsServer: dnsServer)
        try await self.provider.setTunnelNetworkSettings(networkSettings)
    }

    private func makeNetworkSettings(settings: Settings, dnsServer: String) -> NEPacketTunnelNetworkSettings {
        let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "8.8.8.8")
        networkSettings.mtu = 1250
        networkSettings.dnsSettings = self.makeDNSSettings(settings: settings, dnsServer: dnsServer)
        networkSettings.ipv4Settings = self.makeIPv4Settings(settings: settings, dnsServer: dnsServer)
        return networkSettings
    }

    private func makeDNSSettings(settings: Settings, dnsServer: String) -> NEDNSSettings {
        let dnsSettings = NEDNSSettings(servers: [dnsServer])
        dnsSettings.searchDomains = [settings.networkDomain]
        dnsSettings.matchDomains = [settings.networkDomain, ""]
        // 设置为 false 允许系统在补全 Search Domain 时也能匹配到此设置
        dnsSettings.matchDomainsNoSearch = false
        return dnsSettings
    }

    private func makeIPv4Settings(settings: Settings, dnsServer: String) -> NEIPv4Settings {
        let ipv4Settings = NEIPv4Settings(addresses: [settings.ipAddress], subnetMasks: [settings.maskAddress])
        ipv4Settings.includedRoutes = self.makeIPv4IncludedRoutes(settings: settings, dnsServer: dnsServer)
        ipv4Settings.excludedRoutes = self.makeIPv4ExcludedRoutes()
        return ipv4Settings
    }

    private func makeIPv4IncludedRoutes(settings: Settings, dnsServer: String) -> [NEIPv4Route] {
        var routes: [NEIPv4Route] = [
            NEIPv4Route(destinationAddress: settings.netAddress, subnetMask: settings.maskAddress),
            NEIPv4Route(destinationAddress: dnsServer, subnetMask: "255.255.255.255"),
        ]

        if settings.shouldRouteDefault {
            routes.append(.default())
        }

        return routes
    }

    private func makeIPv4ExcludedRoutes() -> [NEIPv4Route] {
        let dnsServers = SDLUtil.getMacOSSystemDnsServers()
        var ipv4DnsServers = dnsServers.filter { !$0.contains(":") }
        let commonDnsServers = [
            "8.8.8.8",
            "8.8.4.4",
            "223.5.5.5",
            "223.6.6.6",
            "114.114.114.114"
        ]

        for ip in commonDnsServers where !ipv4DnsServers.contains(ip) {
            ipv4DnsServers.append(ip)
        }

        return ipv4DnsServers.map {
            NEIPv4Route(destinationAddress: $0, subnetMask: "255.255.255.255")
        }
    }
}
