/**
 * Dev-only self-signed TLS material, generated at boot via the system `openssl`. NEVER for
 * production: a throwaway CA + a browser server cert + an agent-facing server cert signed by that
 * CA. Private keys are written 0600 and never logged (INV9). The dev CA must never be reused
 * anywhere real.
 */
import { execFileSync } from 'node:child_process'
import { mkdtempSync, readFileSync, chmodSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'

export interface DevCerts {
  readonly dir: string
  readonly browserCertPath: string
  readonly browserKeyPath: string
  readonly agentServerCertPath: string
  readonly agentServerKeyPath: string
  readonly caCertPath: string
  readonly caCertPem: string
  cleanup(): void
}

function openssl(args: readonly string[], cwd: string): void {
  execFileSync('openssl', args, { cwd, stdio: ['ignore', 'ignore', 'pipe'] })
}

/**
 * Generate the dev PKI. Uses Ed25519 keys (OpenSSL 3). Throws if `openssl` is unavailable or a
 * step fails — the caller fails fast rather than booting without TLS.
 */
export function generateDevCerts(browserCn: string): DevCerts {
  const dir = mkdtempSync(join(tmpdir(), 'relay-run-certs-'))
  const p = (name: string): string => join(dir, name)

  // Throwaway CA (agent mTLS trust anchor).
  openssl(
    ['req', '-x509', '-newkey', 'ed25519', '-keyout', 'ca.key', '-out', 'ca.crt', '-days', '7',
      '-nodes', '-subj', '/CN=relay-run-dev-ca'],
    dir,
  )

  // Browser-facing server cert (self-signed; the user accepts it once).
  openssl(
    ['req', '-x509', '-newkey', 'ed25519', '-keyout', 'browser.key', '-out', 'browser.crt',
      '-days', '7', '-nodes', '-subj', `/CN=${browserCn}`,
      '-addext', `subjectAltName=DNS:${browserCn},DNS:localhost,IP:127.0.0.1`],
    dir,
  )

  // Agent-facing server cert, signed by the dev CA (the agent pins this CA).
  openssl(
    ['req', '-new', '-newkey', 'ed25519', '-keyout', 'agent-server.key', '-out', 'agent-server.csr',
      '-nodes', '-subj', '/CN=localhost',
      '-addext', 'subjectAltName=DNS:localhost,IP:127.0.0.1'],
    dir,
  )
  openssl(
    ['x509', '-req', '-in', 'agent-server.csr', '-CA', 'ca.crt', '-CAkey', 'ca.key',
      '-CAcreateserial', '-out', 'agent-server.crt', '-days', '7', '-copy_extensions', 'copy'],
    dir,
  )

  for (const key of ['ca.key', 'browser.key', 'agent-server.key']) chmodSync(p(key), 0o600)

  return {
    dir,
    browserCertPath: p('browser.crt'),
    browserKeyPath: p('browser.key'),
    agentServerCertPath: p('agent-server.crt'),
    agentServerKeyPath: p('agent-server.key'),
    caCertPath: p('ca.crt'),
    caCertPem: readFileSync(p('ca.crt'), 'utf8'),
    cleanup() {
      rmSync(dir, { recursive: true, force: true })
    },
  }
}
