import Foundation
import Testing

#if canImport(CryptoKit)
import CryptoKit
#else
import Crypto
#endif

@testable import NucleicProtocol

/// Pins the relay proof-of-possession crypto contract to the Worker (mesh P2). The vector below
/// was produced by `cloud/nucleic-edge/src/relayEnroll.ts`'s exact computation (WebCrypto X25519 +
/// HMAC-SHA256); if the Swift host and the Worker ever diverge on the shared-secret or proof
/// derivation, enrollment silently fails at deploy time — this catches it hermetically.
@Suite struct RelayEnrollmentTests {
    // Generated once from the Worker's crypto (node relayEnroll computation).
    private let staticPrivHex = "b82131c50793010b39fb1f51a954a81e1a68b78ef267e0c59b461b525555d743"
    private let staticPubHex = "988fdc634a981c537e77e59d1f0d4437a1d9228533375a3c57268c1c2dfaaa2b"
    private let ephPubHex = "c1f37a14968c291899ead48009a2b3c7d461a0dcd92203e597dde203cbfd2868"
    private let expectedStaticKeyB64 = "mI_cY0qYHFN-d-WdHw1EN6HZIoUzN1o8VyaMHC36qis"
    private let expectedProofHex = "a9b4ff800406ab55d201674e9331d3a3772a507da1e03102b68f3ae9e69ff4a9"
    private let expectedRoomID = "18c285df4237f63ea82e0af02eef3f10d8692c0b33856c7d925323c5d5b49e12"

    private func hex(_ s: String) -> Data {
        var out = Data(capacity: s.count / 2)
        var i = s.startIndex
        while i < s.endIndex {
            let j = s.index(i, offsetBy: 2)
            out.append(UInt8(s[i..<j], radix: 16)!)
            i = j
        }
        return out
    }

    @Test func staticKeyEncodingMatchesTheWorker() {
        #expect(RelayEnrollment.staticKeyEncoding(hex(staticPubHex)) == expectedStaticKeyB64)
    }

    @Test func proofMatchesTheWorkerVector() throws {
        let staticKey = try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: hex(staticPrivHex))
        // Sanity: the private key we imported derives the expected public key.
        #expect(staticKey.publicKey.rawRepresentation == hex(staticPubHex))

        let proof = try RelayEnrollment.proof(
            staticKey: staticKey,
            ephemeralPublicKey: hex(ephPubHex),
            staticKeyB64: expectedStaticKeyB64)
        #expect(proof == hex(expectedProofHex))
    }

    @Test func hostIDMatchesTheWorkerRoomID() {
        // The relay derives roomId = sha256hex(staticPub); the Swift host must derive the same
        // string as its hostID so the room it advertises is the room it enrolled for.
        #expect(DeviceIdentity.hostID(ofStaticKey: hex(staticPubHex)) == expectedRoomID)
    }

    // MARK: - Runner-pool enrollment (docs/COVALENCE_RUNNER.md §8)

    // Same key material, the runner Worker's context ("nucleic-runner-enroll-v1:"). Vector
    // produced by the identical X25519+HMAC computation cloud/nucleic-runner/src/enroll.ts
    // runs (cross-checked: the relay-context value from the same computation reproduces
    // `expectedProofHex` above byte-for-byte).
    private let expectedRunnerProofHex = "718a5efaae22ddea90990398a75bdf67bc368ae4128ffce3eb06f7ee6cdc74bc"

    @Test func runnerProofMatchesTheWorkerVector() throws {
        let staticKey = try Curve25519.KeyAgreement.PrivateKey(rawRepresentation: hex(staticPrivHex))
        let proof = try RunnerEnrollment.proof(
            staticKey: staticKey,
            ephemeralPublicKey: hex(ephPubHex),
            staticKeyB64: expectedStaticKeyB64)
        #expect(proof == hex(expectedRunnerProofHex))
        // The two services' proofs must differ — the context string is the anti-replay boundary.
        #expect(proof != hex(expectedProofHex))
    }

    @Test func runnerStaticKeyEncodingMatchesTheRelay() {
        // One identity, one spelling: both enrollments send the identical staticKey string.
        #expect(RunnerEnrollment.staticKeyEncoding(hex(staticPubHex)) == expectedStaticKeyB64)
    }
}
