/**
 * A1 — ECDSA-P256 PKCS#10 parse + proof-of-possession, mirroring `ca/csr.ts` (Ed25519 relay path).
 *
 * Hardware-bound host/device keys are P-256 ONLY (Secure Enclave / Android Keystore). This module
 * parses a P-256 PKCS#10 `CertificationRequest`, verifies its self-signature (PoP — the requester
 * holds the private key for the embedded pubkey; `@peculiar`'s `req.verify()` handles ECDSA), and
 * exposes the embedded public key as SubjectPublicKeyInfo DER so the issuer can gate on it.
 *
 * FAIL-CLOSED and UNIFORM: malformed DER, a non-P256 key, or a bad signature ALL return
 * `{ ok: false, embeddedPubSpki: [] }` with no distinguishing detail. Independent of registry state.
 *
 * `reflect-metadata` must load before `@peculiar/x509` (tsyringe polyfill) — keep it first.
 */
import 'reflect-metadata'
import * as x509 from '@peculiar/x509'
import { AsnConvert } from '@peculiar/asn1-schema'
import { SubjectPublicKeyInfo } from '@peculiar/asn1-x509'
import { webcrypto } from 'node:crypto'
import { timingSafeEqualBytes } from '../util/bytes.js'

x509.cryptoProvider.set(webcrypto)

/** OID 1.2.840.10045.2.1 (id-ecPublicKey). */
const OID_EC_PUBLIC_KEY = '1.2.840.10045.2.1'
/** DER of the namedCurve parameter OID prime256v1 / secp256r1 (1.2.840.10045.3.1.7). */
const PRIME256V1_PARAM_DER = Uint8Array.from([
  0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
])

function toArrayBuffer(bytes: Uint8Array): ArrayBuffer {
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer
}

/** True iff `spkiDer` is a well-formed EC P-256 SubjectPublicKeyInfo. Never throws. */
function isP256Spki(spkiDer: Uint8Array): boolean {
  try {
    const spki = AsnConvert.parse(toArrayBuffer(spkiDer), SubjectPublicKeyInfo)
    if (spki.algorithm.algorithm !== OID_EC_PUBLIC_KEY) return false
    const params = spki.algorithm.parameters
    if (!params) return false
    // Reuse the shared constant-time comparison rather than a local short-circuiting one.
    return timingSafeEqualBytes(new Uint8Array(params), PRIME256V1_PARAM_DER)
  } catch {
    return false
  }
}

/**
 * Build a FRESH uniform-failure result per call — never share a module-level singleton (immutability):
 * callers `toEqual`-assert this shape, and a shared instance would let one caller mutate a value another
 * caller later reads.
 */
function uniformFailure(): { ok: false; embeddedPubSpki: Uint8Array } {
  return { ok: false, embeddedPubSpki: new Uint8Array(0) }
}

/**
 * Verify an ECDSA-P256 CSR's proof-of-possession. Parses the PKCS#10 DER, requires an EC P-256
 * embedded key, and checks the self-signature. Returns the embedded pubkey as SPKI DER on success.
 * FAIL-CLOSED and UNIFORM: any malformed input, non-P256 key, or bad signature yields
 * `{ ok: false, embeddedPubSpki: [] }`. Independent of any registry state.
 */
export async function verifyCsrPoPEc(
  csr: Uint8Array,
): Promise<{ ok: boolean; embeddedPubSpki: Uint8Array }> {
  try {
    const req = new x509.Pkcs10CertificateRequest(toArrayBuffer(csr))
    const spkiDer = new Uint8Array(req.publicKey.rawData)
    if (!isP256Spki(spkiDer)) return uniformFailure()
    const ok = await req.verify()
    return ok ? { ok: true, embeddedPubSpki: spkiDer } : uniformFailure()
  } catch {
    return uniformFailure()
  }
}

/**
 * Cheap routing predicate for the `/enroll` fork: true iff `csr` parses as a PKCS#10 whose embedded
 * key is EC P-256. Inspects ONLY the SubjectPublicKeyInfo algorithm — it does NOT verify the
 * self-signature (the native arm's `verifyCsrPoPEc` gate does that after routing). Never throws:
 * a malformed CSR or any non-P256 key (e.g. Ed25519 relay CSRs) yields `false`, so the caller falls
 * through to the Ed25519 relay arm.
 */
export function isEcP256Csr(csr: Uint8Array): boolean {
  try {
    const req = new x509.Pkcs10CertificateRequest(toArrayBuffer(csr))
    return isP256Spki(new Uint8Array(req.publicKey.rawData))
  } catch {
    return false
  }
}

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

export interface BuildCsrEcResult {
  /** The PKCS#10 CertificationRequest DER. */
  readonly der: Uint8Array
  /** The generated P-256 key pair (extractable — TEST ONLY). */
  readonly keys: WebCryptoKeyPair
}

/**
 * TEST HELPER — build a real ECDSA-P256 PKCS#10 CSR self-signed by a freshly generated P-256 key, so
 * tests exercise the real parse/verify path (production CSRs come from hardware unchanged). Mirrors
 * `ca/csr.ts` `buildCsr`. Returns the DER and the generated key pair.
 */
export async function buildCsrEc(subject = 'CN=web-terminal-device'): Promise<BuildCsrEcResult> {
  const keys = (await webcrypto.subtle.generateKey(
    { name: 'ECDSA', namedCurve: 'P-256' },
    true,
    ['sign', 'verify'],
  )) as unknown as WebCryptoKeyPair
  const csr = await x509.Pkcs10CertificateRequestGenerator.create({
    name: subject,
    keys,
    signingAlgorithm: { name: 'ECDSA', hash: 'SHA-256' },
  })
  return { der: new Uint8Array(csr.rawData), keys }
}
