import Foundation
import Security
import Testing
@testable import ClientTLS

// C-iOS · Proves the manual PKCS#10 encoder produces a well-formed, self-signed
// P-256 CSR that the control-plane `verifyCsrPoPEc` (id-ecPublicKey + prime256v1
// SPKI, ecdsa-with-SHA256 self-signature) would accept. Runs headless with a
// SOFTWARE P-256 SecKey (SecKeyCreateRandomKey WITHOUT the Secure-Enclave token)
// so no hardware/entitlement is needed — the signing path is byte-identical to
// the on-device SE key. Real SE keygen + SecIdentity roundtrip are device-only.

/// Software P-256 key via the SAME SecKey API used on-device (no SE token).
private func makeSoftwareKey() throws -> SecureEnclaveKey {
    try SecureEnclaveKeyFactory.generateSoftware(tag: nil, permanent: false)
}

@Test("CSR is a canonical PKCS#10 SEQUENCE of exactly three elements")
func csrOuterStructure() throws {
    // Arrange
    let signer = try makeSoftwareKey()

    // Act
    let der = try CertificateSigningRequest.der(
        subjectCommonName: "web-terminal-device", signer: signer
    )

    // Assert — outer CertificationRequest ::= SEQUENCE { info, algId, sig }.
    let bytes = [UInt8](der)
    let outer = try #require(TestDER.read(bytes, at: 0))
    #expect(outer.tag == 0x30)
    #expect(outer.end == bytes.count) // no trailing garbage
    let parts = TestDER.children(bytes, outer)
    #expect(parts.count == 3)
    #expect(parts[0].tag == 0x30) // certificationRequestInfo
    #expect(parts[1].tag == 0x30) // signatureAlgorithm
    #expect(parts[2].tag == 0x03) // signature BIT STRING
}

@Test("CSR self-signature verifies against the embedded P-256 public key")
func csrSelfSignatureVerifies() throws {
    // Arrange
    let signer = try makeSoftwareKey()
    let expectedPoint = try signer.publicKeyX963()

    // Act
    let der = try CertificateSigningRequest.der(
        subjectCommonName: "web-terminal-device", signer: signer
    )
    let bytes = [UInt8](der)

    // Extract the exact CertificationRequestInfo bytes that were signed and the
    // ECDSA signature (the same crypto check `verifyCsrPoPEc`'s req.verify() runs).
    let outer = try #require(TestDER.read(bytes, at: 0))
    let parts = TestDER.children(bytes, outer)
    let infoBytes = Data(bytes[parts[0].start..<parts[0].end])
    let sigContent = parts[2] // BIT STRING: first content byte is unused-bits (0x00)
    let signature = Data(bytes[(sigContent.valueStart + 1)..<sigContent.valueEnd])

    // Rebuild the public SecKey from the X9.63 point and verify.
    let publicKey = try #require(makePublicKey(fromX963: expectedPoint))
    var error: Unmanaged<CFError>?
    let ok = SecKeyVerifySignature(
        publicKey,
        .ecdsaSignatureMessageX962SHA256,
        infoBytes as CFData,
        signature as CFData,
        &error
    )
    #expect(ok, "self-signature must verify: \(String(describing: error?.takeRetainedValue()))")
}

@Test("CSR embeds a P-256 SubjectPublicKeyInfo the server verifier accepts")
func csrEmbedsP256Spki() throws {
    // Arrange
    let signer = try makeSoftwareKey()
    let point = [UInt8](try signer.publicKeyX963())

    // Act
    let der = try CertificateSigningRequest.der(
        subjectCommonName: "web-terminal-device", signer: signer
    )
    let bytes = [UInt8](der)

    // certificationRequestInfo → { version, subject, subjectPKInfo, [0] attrs }
    let outer = try #require(TestDER.read(bytes, at: 0))
    let info = TestDER.children(bytes, outer)[0]
    let infoChildren = TestDER.children(bytes, info)
    #expect(infoChildren.count == 4)
    #expect(Array(bytes[infoChildren[0].start..<infoChildren[0].end]) == [0x02, 0x01, 0x00]) // v1(0)
    #expect(infoChildren[3].tag == 0xA0) // [0] IMPLICIT attributes
    #expect(infoChildren[3].valueEnd - infoChildren[3].valueStart == 0) // empty SET

    // subjectPublicKeyInfo ::= SEQUENCE { AlgorithmIdentifier, BIT STRING point }
    let spki = infoChildren[2]
    let spkiChildren = TestDER.children(bytes, spki)
    #expect(spkiChildren.count == 2)
    let algIdChildren = TestDER.children(bytes, spkiChildren[0])
    // AlgorithmIdentifier { id-ecPublicKey, prime256v1 } — the exact OIDs
    // verifyCsrPoPEc pins.
    #expect(Array(bytes[algIdChildren[0].start..<algIdChildren[0].end])
        == [0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01])
    #expect(Array(bytes[algIdChildren[1].start..<algIdChildren[1].end])
        == [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07])
    // BIT STRING content = 0x00 unused-bits + the exact 65-byte point.
    let bitString = spkiChildren[1]
    #expect(bitString.tag == 0x03)
    #expect(bytes[bitString.valueStart] == 0x00)
    #expect(Array(bytes[(bitString.valueStart + 1)..<bitString.valueEnd]) == point)
}

@Test("signatureAlgorithm is ecdsa-with-SHA256")
func csrSignatureAlgorithm() throws {
    let signer = try makeSoftwareKey()
    let der = try CertificateSigningRequest.der(
        subjectCommonName: "web-terminal-device", signer: signer
    )
    let bytes = [UInt8](der)
    let outer = try #require(TestDER.read(bytes, at: 0))
    let algId = TestDER.children(bytes, outer)[1]
    let oid = TestDER.children(bytes, algId)[0]
    #expect(Array(bytes[oid.start..<oid.end])
        == [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02])
}

@Test("empty subject CN is rejected")
func csrRejectsEmptySubject() throws {
    let signer = try makeSoftwareKey()
    #expect(throws: CertificateSigningRequest.CSRError.invalidSubject) {
        _ = try CertificateSigningRequest.der(subjectCommonName: "", signer: signer)
    }
}

// MARK: - helpers

/// Reconstruct a public SecKey from an X9.63 uncompressed point for verification.
private func makePublicKey(fromX963 point: Data) -> SecKey? {
    let attributes: [String: Any] = [
        kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
        kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits as String: 256,
    ]
    return SecKeyCreateWithData(point as CFData, attributes as CFDictionary, nil)
}

/// A throwaway DER reader for assertions (production parsing lives in
/// CertificateSummary's X509 walk; this mirrors it for tests).
enum TestDER {
    struct Element {
        let tag: UInt8
        let start: Int // index of the tag byte
        let valueStart: Int
        let valueEnd: Int
        var end: Int { valueEnd }
    }

    static func read(_ bytes: [UInt8], at start: Int) -> Element? {
        guard start >= 0, start + 1 < bytes.count else { return nil }
        let tag = bytes[start]
        var index = start + 1
        let first = bytes[index]
        index += 1
        var length = 0
        if first & 0x80 == 0 {
            length = Int(first)
        } else {
            let count = Int(first & 0x7F)
            guard count > 0, count <= 4, index + count <= bytes.count else { return nil }
            for _ in 0..<count {
                length = (length << 8) | Int(bytes[index])
                index += 1
            }
        }
        let valueEnd = index + length
        guard valueEnd <= bytes.count else { return nil }
        return Element(tag: tag, start: start, valueStart: index, valueEnd: valueEnd)
    }

    static func children(_ bytes: [UInt8], _ parent: Element) -> [Element] {
        var elements: [Element] = []
        var index = parent.valueStart
        while index < parent.valueEnd, let element = read(bytes, at: index) {
            elements.append(element)
            index = element.valueEnd
        }
        return elements
    }
}
