import CryptoKit
import Testing
import Foundation
@testable import Oppi

@Suite("TLSPinning")
struct TLSPinningTests {

    // MARK: - certFingerprint(for:)

    @Test("certFingerprint returns sha256: prefix")
    func certFingerprintPrefix() {
        let data = Data("arbitrary cert bytes".utf8)
        let fp = PinnedServerTrustDelegate.certFingerprint(for: data)
        #expect(fp.hasPrefix("sha256:"))
    }

    @Test("certFingerprint suffix uses base64url alphabet only")
    func certFingerprintBase64URLAlphabet() {
        let data = Data("arbitrary cert bytes".utf8)
        let fp = PinnedServerTrustDelegate.certFingerprint(for: data)
        let suffix = String(fp.dropFirst("sha256:".count))
        #expect(!suffix.isEmpty)
        // base64url uses A-Z, a-z, 0-9, -, _ (no +, /, or =)
        let validChars = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-_"))
        #expect(suffix.unicodeScalars.allSatisfy { validChars.contains($0) })
        #expect(!suffix.contains("+"))
        #expect(!suffix.contains("/"))
        #expect(!suffix.contains("="))
    }

    @Test("certFingerprint is deterministic for same input")
    func certFingerprintDeterministic() {
        let data = Data("deterministic test".utf8)
        let fp1 = PinnedServerTrustDelegate.certFingerprint(for: data)
        let fp2 = PinnedServerTrustDelegate.certFingerprint(for: data)
        #expect(fp1 == fp2)
    }

    @Test("certFingerprint produces different values for different inputs")
    func certFingerprintDistinct() {
        let fp1 = PinnedServerTrustDelegate.certFingerprint(for: Data("cert-a".utf8))
        let fp2 = PinnedServerTrustDelegate.certFingerprint(for: Data("cert-b".utf8))
        #expect(fp1 != fp2)
    }

    @Test("certFingerprint matches independent SHA256 computation")
    func certFingerprintMatchesSHA256() {
        let data = Data("self-signed cert DER content".utf8)
        let digest = Data(SHA256.hash(data: data))
        let expected = "sha256:\(digest.base64EncodedString().replacingOccurrences(of: "+", with: "-").replacingOccurrences(of: "/", with: "_").replacingOccurrences(of: "=", with: ""))"
        let actual = PinnedServerTrustDelegate.certFingerprint(for: data)
        #expect(actual == expected)
    }

    @Test("certFingerprint handles empty data")
    func certFingerprintEmptyData() {
        let fp = PinnedServerTrustDelegate.certFingerprint(for: Data())
        // SHA256 of empty string is a known value; just verify format
        #expect(fp.hasPrefix("sha256:"))
        let suffix = String(fp.dropFirst("sha256:".count))
        #expect(!suffix.isEmpty)
    }

    // MARK: - PinnedServerTrustDelegate init

    @Test("delegate initialises without crashing when fingerprint is nil")
    func delegateInitNilFingerprint() {
        _ = PinnedServerTrustDelegate(pinnedLeafFingerprint: nil)
    }

    @Test("delegate initialises without crashing for valid fingerprint")
    func delegateInitValidFingerprint() {
        let data = Data("sample cert".utf8)
        let fp = PinnedServerTrustDelegate.certFingerprint(for: data)
        _ = PinnedServerTrustDelegate(pinnedLeafFingerprint: fp)
    }

    @Test("delegate initialises without crashing for whitespace-only fingerprint")
    func delegateInitWhitespaceFingerprint() {
        // Whitespace-only should be treated as nil (no pinning)
        _ = PinnedServerTrustDelegate(pinnedLeafFingerprint: "   ")
    }

    // MARK: - Fingerprint known-value verification

    @Test("certFingerprint for empty data matches known SHA256 of empty input")
    func certFingerprintEmptyDataKnownValue() {
        // SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
        let fp = PinnedServerTrustDelegate.certFingerprint(for: Data())
        let digest = Data(SHA256.hash(data: Data()))
        let expected = "sha256:" + digest.base64EncodedString()
            .replacingOccurrences(of: "+", with: "-")
            .replacingOccurrences(of: "/", with: "_")
            .replacingOccurrences(of: "=", with: "")
        #expect(fp == expected)
    }

    @Test("certFingerprint for large cert data stays within expected length")
    func certFingerprintLargeData() {
        // Typical DER cert is 1-4 KB. SHA256 always produces 32 bytes
        // -> base64url = ceil(32/3)*4 = 44 chars, minus padding = 43 chars
        let largeCert = Data(repeating: 0xAB, count: 4096)
        let fp = PinnedServerTrustDelegate.certFingerprint(for: largeCert)
        let suffix = String(fp.dropFirst("sha256:".count))
        // base64url of 32 bytes is 43 chars (no padding)
        #expect(suffix.count == 43)
    }

    @Test("certFingerprint for binary data with base64 special chars uses url-safe encoding")
    func certFingerprintBinaryEdgeCase() {
        // Find a payload whose SHA256 base64 contains +, /, or = to verify conversion.
        // Use a known payload: Data([0xFF]) produces a digest whose base64 we can check.
        let data = Data([0xFF])
        let fp = PinnedServerTrustDelegate.certFingerprint(for: data)
        let suffix = String(fp.dropFirst("sha256:".count))
        // Regardless of which chars appear, they must all be url-safe
        let validChars = CharacterSet.alphanumerics.union(CharacterSet(charactersIn: "-_"))
        #expect(suffix.unicodeScalars.allSatisfy { validChars.contains($0) })
    }

    // MARK: - Normalize fingerprint edge cases

    @Test("delegate normalizes leading/trailing whitespace in fingerprint")
    func delegateNormalizesWhitespace() {
        let certData = Data("normalize test".utf8)
        let fp = PinnedServerTrustDelegate.certFingerprint(for: certData)
        // Wrapping with whitespace should still create a valid delegate
        // that would match the same cert — we verify it doesn't crash
        // and stores the trimmed version by checking fingerprint format.
        let delegate = PinnedServerTrustDelegate(pinnedLeafFingerprint: "  \(fp)  ")
        // If it stored the raw string with spaces, it would never match.
        // We can't directly inspect the stored value, but we verify it
        // was created without error.
        _ = delegate
    }

    @Test("delegate treats empty string fingerprint as nil (no pinning)")
    func delegateEmptyStringFingerprint() {
        // Empty string should be normalized to nil — same behavior as no pinning
        _ = PinnedServerTrustDelegate(pinnedLeafFingerprint: "")
    }

    @Test("public CA fallback is allowed for Tailscale hostnames")
    func publicCAFallbackAllowedForTailscaleHosts() {
        #expect(PinnedServerTrustDelegate.allowsPublicCATrustFallback(forHost: "mac-studio.tail123.ts.net"))
        #expect(PinnedServerTrustDelegate.allowsPublicCATrustFallback(forHost: "NODE.beta.tailscale.net"))
    }

    @Test("configured pin remains authoritative for Tailscale hostnames")
    func publicCAFallbackRejectsPinnedTailscaleHost() {
        #expect(!PinnedServerTrustDelegate.allowsPublicCATrustFallback(
            forHost: "mac-studio.tail123.ts.net",
            pinnedLeafFingerprint: "sha256:stable-pin"
        ))
    }

    @Test("public CA fallback is not allowed for arbitrary hosts")
    func publicCAFallbackRejectsNonTailscaleHosts() {
        #expect(!PinnedServerTrustDelegate.allowsPublicCATrustFallback(forHost: "192.168.68.66"))
        #expect(!PinnedServerTrustDelegate.allowsPublicCATrustFallback(forHost: "example.com"))
        #expect(!PinnedServerTrustDelegate.allowsPublicCATrustFallback(forHost: "evil-ts.net.example.com"))
    }
}
