/**
 * A4 (FIX C-2) — P-256 device leaf issuance off the device-CA via `assembleCertificate`. Proves the
 * leaf re-parses + verifies against the device-CA public key; carries SAN dNSName `<sub>.terminal.yaojia.wang`
 * + the device SPIFFE URI (`.../device/<did>`, parseable by relay-auth as kind 'device'); is
 * clientAuth / CA:false; and that the registry gate rejects uniformly (unknown / revoked / pubkey mismatch).
 */
import 'reflect-metadata'
import { describe, test, expect } from 'vitest'
import * as x509 from '@peculiar/x509'
import { webcrypto } from 'node:crypto'
import { parseSpiffeId } from 'relay-auth/src/agent/spiffe.js'
import { buildCsrEc } from '../src/ca/csr-ec.js'
import { inProcessP256CaSigner } from '../src/boot/ca-wiring.js'
import { createDeviceLeafSigner, DeviceLeafSignError } from '../src/ca/device-issue.js'
import { createMemoryDeviceStore, type DeviceRecord } from '../src/registry/devices.js'

x509.cryptoProvider.set(webcrypto)

const SAN_BASE = 'terminal.yaojia.wang'
const TRUST_DOMAIN = 'example.com'
const ACCOUNT_A = 'a1'

async function spkiOf(pub: CryptoKey): Promise<Uint8Array> {
  return new Uint8Array(await webcrypto.subtle.exportKey('spki', pub))
}

function record(overrides: Partial<DeviceRecord>): DeviceRecord {
  const now = Date.now()
  return {
    deviceId: 'dev-1',
    accountId: ACCOUNT_A,
    subdomainScope: 'alice',
    ecPubkeySpki: new Uint8Array([1, 2, 3]),
    serial: '0102030405060708090a0b0c0d0e0f10',
    status: 'active',
    notAfter: new Date(now + 24 * 60 * 60 * 1000).toISOString(),
    attestationLevel: 'none',
    createdAt: new Date(now).toISOString(),
    revokedAt: null,
    ...overrides,
  }
}

async function fixture() {
  const ca = inProcessP256CaSigner()
  const store = createMemoryDeviceStore()
  const signer = createDeviceLeafSigner({
    signer: ca,
    issuer: 'CN=device-CA',
    caChainDer: [ca.publicKeyRaw],
    sanBaseDomain: SAN_BASE,
    trustDomain: TRUST_DOMAIN,
    devices: store,
  })
  const { der: csr, keys } = await buildCsrEc('CN=web-terminal-device')
  const embeddedSpki = await spkiOf(keys.publicKey)
  return { ca, store, signer, csr, embeddedSpki }
}

async function importP256Public(spkiDer: Uint8Array): Promise<CryptoKey> {
  return webcrypto.subtle.importKey('spki', new Uint8Array(spkiDer), { name: 'ECDSA', namedCurve: 'P-256' }, true, [
    'verify',
  ])
}

describe('A4 device-issue — real P-256 device leaf', () => {
  test('leaf re-parses and verifies against the device-CA public key', async () => {
    const { ca, store, signer, csr, embeddedSpki } = await fixture()
    await store.insert(record({ deviceId: 'dev-1', ecPubkeySpki: embeddedSpki }))
    const { cert } = await signer.signDeviceLeaf('dev-1', csr)
    const leaf = new x509.X509Certificate(cert)
    expect(() => new x509.X509Certificate(cert)).not.toThrow()
    const caPub = await importP256Public(ca.publicKeyRaw)
    expect(await leaf.verify({ publicKey: caPub, signatureOnly: true })).toBe(true)
  })

  test('SAN carries dNSName <sub>.terminal.yaojia.wang + the device SPIFFE URI', async () => {
    const { store, signer, csr, embeddedSpki } = await fixture()
    await store.insert(record({ deviceId: 'dev-1', accountId: ACCOUNT_A, subdomainScope: 'alice', ecPubkeySpki: embeddedSpki }))
    const { cert } = await signer.signDeviceLeaf('dev-1', csr)
    const san = new x509.X509Certificate(cert).getExtension(x509.SubjectAlternativeNameExtension)
    const names = san!.names.toJSON()
    expect(names).toContainEqual({ type: 'dns', value: 'alice.terminal.yaojia.wang' })
    const uri = names.find((n) => n.type === 'url')!.value
    const parsed = parseSpiffeId(uri)
    expect(parsed).toEqual({ accountId: ACCOUNT_A, kind: 'device', id: 'dev-1' })
  })

  test('leaf is EKU clientAuth and CA:false', async () => {
    const { store, signer, csr, embeddedSpki } = await fixture()
    await store.insert(record({ deviceId: 'dev-1', ecPubkeySpki: embeddedSpki }))
    const { cert } = await signer.signDeviceLeaf('dev-1', csr)
    const leaf = new x509.X509Certificate(cert)
    const bc = leaf.getExtension(x509.BasicConstraintsExtension)
    expect(bc!.ca).toBe(false)
    const eku = leaf.getExtension(x509.ExtendedKeyUsageExtension)
    expect(eku!.usages).toContain(x509.ExtendedKeyUsage.clientAuth)
  })

  test('result reports serial + validity from the gated record', async () => {
    const { store, signer, csr, embeddedSpki } = await fixture()
    const rec = record({ deviceId: 'dev-1', ecPubkeySpki: embeddedSpki })
    await store.insert(rec)
    const res = await signer.signDeviceLeaf('dev-1', csr)
    expect(res.serial).toBe(rec.serial)
    expect(res.notAfter.toISOString()).toBe(rec.notAfter)
    expect(res.notBefore.getTime()).toBeLessThan(Date.now())
  })

  test('gate rejects an UNKNOWN device uniformly', async () => {
    const { signer, csr } = await fixture()
    await expect(signer.signDeviceLeaf('ghost', csr)).rejects.toBeInstanceOf(DeviceLeafSignError)
  })

  test('gate rejects a REVOKED device uniformly', async () => {
    const { store, signer, csr, embeddedSpki } = await fixture()
    await store.insert(record({ deviceId: 'dev-1', status: 'revoked', revokedAt: new Date().toISOString(), ecPubkeySpki: embeddedSpki }))
    await expect(signer.signDeviceLeaf('dev-1', csr)).rejects.toBeInstanceOf(DeviceLeafSignError)
  })

  test('gate rejects a pubkey MISMATCH (CSR key ≠ registered key) uniformly', async () => {
    const { store, signer, csr } = await fixture()
    // register with a DIFFERENT pubkey than the CSR embeds
    await store.insert(record({ deviceId: 'dev-1', ecPubkeySpki: new Uint8Array([7, 7, 7, 7]) }))
    await expect(signer.signDeviceLeaf('dev-1', csr)).rejects.toBeInstanceOf(DeviceLeafSignError)
  })

  test('gate rejects a malformed CSR uniformly', async () => {
    const { store, signer, embeddedSpki } = await fixture()
    await store.insert(record({ deviceId: 'dev-1', ecPubkeySpki: embeddedSpki }))
    await expect(signer.signDeviceLeaf('dev-1', new Uint8Array([0, 1, 2, 3]))).rejects.toBeInstanceOf(DeviceLeafSignError)
  })
})
