/**
 * A4 (FIX C-2) — P-256 DEVICE leaf issuance off the device-CA, via the single `assembleCertificate`
 * primitive (KMS-signed TBS). This is the data-path identity the app presents on the existing mTLS
 * path; it is a SEPARATE trust root from the Ed25519 relay agent-CA and the P-256 frp-client-CA
 * (§1.3). Modeled on `ca/issue.ts` (`createRealLeafSigner`) but:
 *   - subject key is EC P-256 (Secure Enclave / Android Keystore are P-256 only);
 *   - the CA signs via `CaSigner.sign()` (device-CA hot signer, custody behind KMS — §5), sigAlg
 *     `ecdsa-p256`;
 *   - SAN = dNSName `<sub>.<baseDomain>` (the nginx :8470 enforcement key, FIX H-2) + URI
 *     `spiffe://relay.<trustDomain>/account/<a>/device/<d>` (identity/audit), built with relay-auth's
 *     OWN `spiffeIdFor(..., 'device')` so the SAN can never drift from the verifier's parser.
 *
 * Registry-gated like `assertLeafGate` (INV14): CSR proof-of-possession + device bound/active/
 * non-revoked + CSR-key == registered-key. Any failure rejects UNIFORMLY (`DeviceLeafSignError`),
 * never leaking which check failed; issuance is unreachable when any check fails. Serial + validity
 * are read from the gated record (the record is authoritative for expiry/CRL pairing).
 *
 * `reflect-metadata` must load before `@peculiar/x509` (tsyringe polyfill) — keep it first.
 */
import 'reflect-metadata'
import * as x509 from '@peculiar/x509'
import { webcrypto } from 'node:crypto'
import { spiffeIdFor } from 'relay-auth/src/agent/spiffe.js'
import type { CaSigner } from '../boot/ca-wiring.js'
import type { DeviceStore } from '../store/ports.js'
import type { DeviceRecord } from '../registry/devices.js'
import { assembleCertificate } from './x509-assembler.js'
import { verifyCsrPoPEc } from './csr-ec.js'
import { timingSafeEqualBytes } from '../util/bytes.js'

x509.cryptoProvider.set(webcrypto)

/** Backdate notBefore to tolerate small clock skew between control-plane and the mTLS terminator. */
const CLOCK_SKEW_SEC = 60

/** Uniform device-leaf gate reject — never leaks which check failed (mirrors `LeafSignError`). */
export class DeviceLeafSignError extends Error {
  constructor(public readonly code: 'csr_rejected' | 'not_registered') {
    super('device leaf signing refused') // uniform message
    this.name = 'DeviceLeafSignError'
  }
}

export interface DeviceLeafResult {
  readonly cert: Uint8Array
  readonly caChain: readonly Uint8Array[]
  readonly serial: string
  readonly notBefore: Date
  readonly notAfter: Date
}

export interface DeviceLeafSigner {
  /** Gate on the device registry, then issue the P-256 leaf for the gated record. */
  signDeviceLeaf(deviceId: string, csr: Uint8Array): Promise<DeviceLeafResult>
}

export interface DeviceLeafSignerDeps {
  /** Device-CA P-256 signer (KMS-shaped; `inProcessP256CaSigner` in dev/tests). */
  readonly signer: CaSigner
  /** Device-CA subject as an `x509.Name`/DN string — used verbatim as the leaf issuer. */
  readonly issuer: x509.Name | string
  /** DER of [device-CA, root] returned to the app as its CA bundle. */
  readonly caChainDer: readonly Uint8Array[]
  /** Base domain for the dNSName SAN: `<sub>.<sanBaseDomain>` (e.g. `terminal.yaojia.wang`). */
  readonly sanBaseDomain: string
  /** Bare trust domain; the SPIFFE builder prepends `relay.`. */
  readonly trustDomain: string
  /** Device registry (gate source of truth). */
  readonly devices: DeviceStore
}

/** Parse a hex serial into big-endian bytes for the certificate serialNumber. */
function hexToBytes(hex: string): Uint8Array {
  return new Uint8Array(Buffer.from(hex, 'hex'))
}

/**
 * The registry + proof-of-possession gate for a device leaf (INV14). Ordered checks, SAME reject:
 *   1. CSR PoP — valid P-256 PKCS#10 self-signature over its embedded key.
 *   2. device bound + active (non-revoked).
 *   3. CSR-embedded key == the device's REGISTERED public key (no substitution).
 * Returns the gated record + the embedded SPKI so the issuer builds the SAN from authoritative data.
 */
export async function assertDeviceLeafGate(
  devices: DeviceStore,
  deviceId: string,
  csr: Uint8Array,
): Promise<{ record: DeviceRecord; embeddedPubSpki: Uint8Array }> {
  const pop = await verifyCsrPoPEc(csr)
  if (!pop.ok) throw new DeviceLeafSignError('csr_rejected')
  const record = await devices.get(deviceId)
  if (record === null || record.status === 'revoked') throw new DeviceLeafSignError('not_registered')
  if (!timingSafeEqualBytes(record.ecPubkeySpki, pop.embeddedPubSpki)) {
    throw new DeviceLeafSignError('not_registered')
  }
  return { record, embeddedPubSpki: pop.embeddedPubSpki }
}

/**
 * Build the device-leaf signer. Each leaf: X.509 v3, EC P-256 subject = the CSR-embedded key,
 * SAN dNSName `<sub>.<baseDomain>` + device SPIFFE URI, CA:false, KeyUsage digitalSignature, EKU
 * clientAuth, validity [now-skew, record.notAfter], serial = record.serial, signed by the device-CA.
 */
export function createDeviceLeafSigner(deps: DeviceLeafSignerDeps): DeviceLeafSigner {
  return {
    async signDeviceLeaf(deviceId, csr) {
      const { record, embeddedPubSpki } = await assertDeviceLeafGate(deps.devices, deviceId, csr)

      const dnsName = `${record.subdomainScope}.${deps.sanBaseDomain}`
      const spiffe = spiffeIdFor(record.accountId, record.deviceId, deps.trustDomain, 'device')
      const notBefore = new Date(Date.now() - CLOCK_SKEW_SEC * 1000)
      const notAfter = new Date(record.notAfter)

      const cert = await assembleCertificate({
        subjectPublicKey: embeddedPubSpki, // SPKI DER of the hardware-bound P-256 key
        subject: `CN=${record.deviceId}`,
        issuer: deps.issuer,
        serialNumber: hexToBytes(record.serial),
        notBefore,
        notAfter,
        extensions: [
          new x509.SubjectAlternativeNameExtension([
            { type: 'dns', value: dnsName },
            { type: 'url', value: spiffe },
          ]),
          new x509.BasicConstraintsExtension(false, undefined, true),
          new x509.KeyUsagesExtension(x509.KeyUsageFlags.digitalSignature, true),
          new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage.clientAuth]),
        ],
        signer: deps.signer,
        sigAlg: 'ecdsa-p256',
      })

      return { cert, caChain: deps.caChainDer, serial: record.serial, notBefore, notAfter }
    },
  }
}
