/**
 * A1 acceptance — ECDSA-P256 PKCS#10 parse + proof-of-possession (`ca/csr-ec.ts`), mirroring the
 * Ed25519 `ca/csr.ts` tests. Gate (d): a valid P-256 CSR yields `ok:true` with the correct embedded
 * pubkey; any tampered / mismatched / non-P256 / malformed CSR yields the UNIFORM `ok:false` result
 * with an empty embedded pubkey and no distinguishing detail (fail-closed).
 */
import 'reflect-metadata'
import { describe, test, expect } from 'vitest'
import * as x509 from '@peculiar/x509'
import { webcrypto } from 'node:crypto'
import { verifyCsrPoPEc, buildCsrEc } from '../src/ca/csr-ec.js'

x509.cryptoProvider.set(webcrypto)

/** WebCrypto key pair (the `CryptoKeyPair` global is not in this project's TS lib set). */
type KeyPair = { readonly publicKey: CryptoKey; readonly privateKey: CryptoKey }

describe('verifyCsrPoPEc — gate (d): valid P-256 CSR', () => {
  test('a valid P-256 CSR → ok:true with the embedded SPKI matching the generating key', async () => {
    const { der, keys } = await buildCsrEc('CN=alice')
    const res = await verifyCsrPoPEc(der)
    expect(res.ok).toBe(true)
    expect(res.embeddedPubSpki.length).toBeGreaterThan(0)
    const exported = new Uint8Array(await webcrypto.subtle.exportKey('spki', keys.publicKey))
    expect(Buffer.from(res.embeddedPubSpki).equals(Buffer.from(exported))).toBe(true)
  })

  test('the embedded SPKI re-imports as an EC P-256 verifying key', async () => {
    const { der } = await buildCsrEc('CN=bob')
    const { embeddedPubSpki } = await verifyCsrPoPEc(der)
    const key = await webcrypto.subtle.importKey('spki', new Uint8Array(embeddedPubSpki), { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify'])
    expect(key.type).toBe('public')
  })
})

describe('verifyCsrPoPEc — fail-closed & uniform rejection', () => {
  test('a tampered CSR (flipped signature byte) → ok:false uniform', async () => {
    const { der } = await buildCsrEc('CN=carol')
    const tampered = new Uint8Array(der)
    tampered[tampered.length - 5]! ^= 0xff
    const res = await verifyCsrPoPEc(tampered)
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('a CSR whose embedded key was swapped (PoP mismatch) → ok:false uniform', async () => {
    // Splice: take request-A's body but leave request-B's signature by concatenating mismatched
    // parts is fragile; instead flip several bytes inside the public-key region to break PoP.
    const { der } = await buildCsrEc('CN=dave')
    const mangled = new Uint8Array(der)
    // Corrupt a byte early in the structure (subject/pubkey area) so the self-signature no longer
    // matches the tbs — still parseable DER shape but PoP fails.
    mangled[25]! ^= 0xff
    const res = await verifyCsrPoPEc(mangled)
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('malformed / non-DER garbage → ok:false uniform (no throw)', async () => {
    const res = await verifyCsrPoPEc(Uint8Array.from([1, 2, 3, 4, 5]))
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('empty input → ok:false uniform', async () => {
    const res = await verifyCsrPoPEc(new Uint8Array(0))
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('a valid non-P256 CSR (Ed25519 key) → ok:false uniform (curve enforced)', async () => {
    const keys = (await webcrypto.subtle.generateKey({ name: 'Ed25519' }, true, ['sign', 'verify'])) as unknown as KeyPair
    const csr = await x509.Pkcs10CertificateRequestGenerator.create({
      name: 'CN=ed-device',
      keys,
      signingAlgorithm: { name: 'Ed25519' },
    })
    const res = await verifyCsrPoPEc(new Uint8Array(csr.rawData))
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('a valid P-384 CSR → ok:false uniform (only P-256 accepted)', async () => {
    const keys = (await webcrypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-384' }, true, ['sign', 'verify'])) as unknown as KeyPair
    const csr = await x509.Pkcs10CertificateRequestGenerator.create({
      name: 'CN=p384-device',
      keys,
      signingAlgorithm: { name: 'ECDSA', hash: 'SHA-384' },
    })
    const res = await verifyCsrPoPEc(new Uint8Array(csr.rawData))
    expect(res).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
  })

  test('CP3: each rejection returns a FRESH failure object (no shared module-level singleton)', async () => {
    const a = await verifyCsrPoPEc(new Uint8Array(0))
    const b = await verifyCsrPoPEc(new Uint8Array(0))
    expect(a).toEqual({ ok: false, embeddedPubSpki: new Uint8Array(0) })
    // Distinct instances (result AND its embedded array) so one caller can never mutate another's.
    expect(a).not.toBe(b)
    expect(a.embeddedPubSpki).not.toBe(b.embeddedPubSpki)
  })
})
