import Foundation
import Testing
import WireProtocol

// T-iOS-3 · HostEndpoint originHeader/wsURL 派生向量（含原 T-iOS-7 用例，plan §7）。
// Origin 铁律：单点派生，禁止手拼（plan §5.1）；默认端口省略与浏览器 Origin 序列化一致。

private func endpoint(_ urlString: String) -> HostEndpoint? {
    guard let url = URL(string: urlString) else { return nil }
    return HostEndpoint(baseURL: url)
}

// MARK: - originHeader 派生向量

@Test("originHeader：http + IPv4 + 非默认端口 → 与 baseURL 同串")
func originHeaderKeepsNonDefaultPortIPv4() throws {
    let sut = try #require(endpoint("http://192.168.1.5:3000"))
    #expect(sut.originHeader == "http://192.168.1.5:3000")
}

@Test("originHeader：https + 非标端口保留端口")
func originHeaderKeepsNonStandardHTTPSPort() throws {
    let sut = try #require(endpoint("https://mac.example:8443"))
    #expect(sut.originHeader == "https://mac.example:8443")
}

@Test("originHeader：https + 443 → 无端口后缀（浏览器 Origin 序列化）")
func originHeaderOmitsDefaultHTTPSPort() throws {
    let sut = try #require(endpoint("https://mac.example:443"))
    #expect(sut.originHeader == "https://mac.example")
}

@Test("originHeader：http + 80 → 无端口后缀")
func originHeaderOmitsDefaultHTTPPort() throws {
    let sut = try #require(endpoint("http://mac.example:80"))
    #expect(sut.originHeader == "http://mac.example")
}

@Test("originHeader：无端口 URL → 无端口后缀；localhost 保留非默认端口")
func originHeaderWithoutExplicitPort() throws {
    let bare = try #require(endpoint("https://mac.tailnet-1234.ts.net"))
    #expect(bare.originHeader == "https://mac.tailnet-1234.ts.net")
    let localhost = try #require(endpoint("http://localhost:3000"))
    #expect(localhost.originHeader == "http://localhost:3000")
}

@Test("originHeader：scheme 与 host 小写规范化（服务器两侧 new URL() 规范化对称）")
func originHeaderLowercasesSchemeAndHost() throws {
    let sut = try #require(endpoint("HTTP://Mac.Example:3000"))
    #expect(sut.originHeader == "http://mac.example:3000")
}

@Test("originHeader：baseURL 的 path/query 不进入 Origin")
func originHeaderIgnoresPathAndQuery() throws {
    let sut = try #require(endpoint("http://192.168.1.5:3000/index.html?x=1"))
    #expect(sut.originHeader == "http://192.168.1.5:3000")
}

@Test("originHeader：IPv6 host 保留方括号")
func originHeaderBracketsIPv6Host() throws {
    let sut = try #require(endpoint("http://[fe80::1]:3000"))
    #expect(sut.originHeader == "http://[fe80::1]:3000")
}

// MARK: - wsURL 派生向量（scheme http→ws / https→wss + WireConstants.wsPath）

@Test("wsURL：http → ws 同 host 同 port + /term")
func wsURLDerivesWsFromHttp() throws {
    let sut = try #require(endpoint("http://192.168.1.5:3000"))
    #expect(sut.wsURL.absoluteString == "ws://192.168.1.5:3000/term")
}

@Test("wsURL：https → wss + /term（非标端口保留）")
func wsURLDerivesWssFromHttps() throws {
    let sut = try #require(endpoint("https://mac.example:8443"))
    #expect(sut.wsURL.absoluteString == "wss://mac.example:8443/term")
}

@Test("wsURL：无端口 https → wss 无端口；path 恒为 WireConstants.wsPath")
func wsURLWithoutPort() throws {
    let sut = try #require(endpoint("https://mac.tailnet-1234.ts.net"))
    #expect(sut.wsURL.absoluteString == "wss://mac.tailnet-1234.ts.net/term")
    #expect(sut.wsURL.path == WireConstants.wsPath)
}

@Test("wsURL：baseURL 带尾斜杠/path/query 时仍只保留 /term")
func wsURLReplacesPathAndDropsQuery() throws {
    let sut = try #require(endpoint("http://192.168.1.5:3000/launcher?join=abc"))
    #expect(sut.wsURL.absoluteString == "ws://192.168.1.5:3000/term")
}

// MARK: - 输入边界：非 http(s) / 无 host 拒绝（扫码结果是不可信输入，plan §5）

@Test("init：非 http(s) scheme 或无 host → nil",
      arguments: ["ftp://mac.example", "file:///tmp/x", "mailto:hi@example.com", "http://", "ws://mac.example:3000"])
func initRejectsNonHTTPBaseURLs(urlString: String) {
    #expect(endpoint(urlString) == nil)
}

// MARK: - Codable（HostRegistry Keychain 持久化经由它）

@Test("Codable roundtrip：encode→decode 恒等")
func codableRoundtripPreservesEquality() throws {
    // Arrange
    let original = try #require(endpoint("https://mac.example:8443"))

    // Act
    let data = try JSONEncoder().encode(original)
    let decoded = try JSONDecoder().decode(HostEndpoint.self, from: data)

    // Assert
    #expect(decoded == original)
    #expect(decoded.originHeader == original.originHeader)
    #expect(decoded.wsURL == original.wsURL)
}

@Test("Codable：持久化数据被篡改成非 http(s) URL → decode 显式 throw（边界再验证）")
func codableDecodeRevalidatesBaseURL() {
    // Arrange
    let tampered = Data("{\"baseURL\":\"ftp://mac.example\"}".utf8)

    // Act / Assert
    #expect(throws: DecodingError.self) {
        _ = try JSONDecoder().decode(HostEndpoint.self, from: tampered)
    }
}
