import Foundation
import Testing
import WireProtocol
import APIClient

/// T-iOS-38 · 四层 host 分级（plan §5.4 提示分层表），公开 API：
/// loopback / privateLAN / tailscale / public。W3 时 PairingViewModel 因
/// `isPrivateOrLocalHost` 是 internal 单桶谓词而自行重写了分类——本类型
/// 是给 VM 换用的单一出处（本任务不改 VM，见日志条目）。
/// 注意本文件**不用 @testable**：证明 API 确实 public。
struct HostClassificationTests {
    @Test("loopback 层:localhost/127.0.0.0\u{2044}8/IPv6 ::1(裸与带括号)")
    func loopbackTierMatchesLocalTargets() {
        // Arrange
        let hosts = ["localhost", "LOCALHOST", "127.0.0.1", "127.8.8.8", "::1", "[::1]"]

        // Act + Assert
        for host in hosts {
            #expect(HostClassifier.classify(host: host) == .loopback, "\(host) 应为 loopback")
        }
    }

    @Test("privateLAN 层:RFC1918 三段 + link-local 169.254\u{2044}16 + mDNS .local")
    func privateLANTierMatchesRFC1918AndLinkLocal() {
        // Arrange
        let hosts = [
            "10.0.0.5", "192.168.0.9", "172.16.0.1", "172.31.255.255",
            "169.254.1.1", "mac-mini.local", "Mac-Mini.LOCAL",
        ]

        // Act + Assert
        for host in hosts {
            #expect(HostClassifier.classify(host: host) == .privateLAN, "\(host) 应为 privateLAN")
        }
    }

    @Test("tailscale 层:CGNAT 100.64.0.0\u{2044}10 与 MagicDNS *.ts.net")
    func tailscaleTierMatchesCGNATAndMagicDNS() {
        // Arrange
        let hosts = [
            "100.64.0.1", "100.100.1.1", "100.127.255.255",
            "mac.tailnet.ts.net", "foo.TS.NET",
        ]

        // Act + Assert
        for host in hosts {
            #expect(HostClassifier.classify(host: host) == .tailscale, "\(host) 应为 tailscale")
        }
    }

    @Test("public 层:公网 IP/域名/越界 CIDR/畸形 IPv4 一律最强警告层")
    func publicTierIsTheFailSafeDefault() {
        // Arrange — 越界:172.32 超出 172.16/12;100.128 与 100.63 超出 100.64/10
        let hosts = [
            "8.8.8.8", "203.0.113.7", "example.com", "tsnet.example.com",
            "172.32.0.1", "100.128.0.1", "100.63.255.255", "256.1.1.1", "1.2.3", "",
        ]

        // Act + Assert — 分不清 → public(fail-safe:宁可多警告,plan §5.4)
        for host in hosts {
            #expect(HostClassifier.classify(host: host) == .public, "\(host) 应为 public")
        }
    }

    @Test("classify(endpoint:) 便捷入口与 host 字符串版一致(VM 只有 HostEndpoint 可用)")
    func endpointConvenienceMatchesHostClassification() throws {
        // Arrange
        let vectors: [(url: String, tier: HostNetworkTier)] = [
            ("http://127.0.0.1:3000", .loopback),
            ("http://192.168.1.5:3000", .privateLAN),
            ("http://100.100.1.1:3000", .tailscale),
            ("https://mac.tailnet.ts.net", .tailscale),
            ("https://example.com", .public),
        ]

        // Act + Assert
        for vector in vectors {
            let url = try #require(URL(string: vector.url))
            let endpoint = try #require(HostEndpoint(baseURL: url))
            #expect(HostClassifier.classify(endpoint: endpoint) == vector.tier, "\(vector.url)")
        }
    }
}
