/**
 * T5 · Passkey/WebAuthn registration ceremony. Passkey is PRIMARY (phishing-resistant; the payload
 * is a root-capable shell). Challenges are CSPRNG, short-TTL, single-use, never logged (INV9).
 */
import { randomBytes } from 'node:crypto'
import { encodeBase64UrlBytes } from 'relay-contracts'
import type { WebAuthnCredential } from '../../types.js'
import {
  WebAuthnError,
  type RegistrationOptions,
  type RegistrationResponse,
  type WebAuthnVerifier,
} from './verifier.js'

const CHALLENGE_BYTES = 32
const DEFAULT_TIMEOUT_MS = 60_000

export function newChallenge(): string {
  return encodeBase64UrlBytes(new Uint8Array(randomBytes(CHALLENGE_BYTES)))
}

export function startRegistration(accountId: string, rpId: string): Promise<RegistrationOptions> {
  return Promise.resolve({
    rpId,
    challenge: newChallenge(),
    userId: accountId,
    timeoutMs: DEFAULT_TIMEOUT_MS,
  })
}

/** Verify attestation and mint the stored credential. `rpId` is bound to the tenant subdomain. */
export async function finishRegistration(
  accountId: string,
  resp: RegistrationResponse,
  expectedChallenge: string,
  rpId: string,
  origin: string,
  verifier: WebAuthnVerifier,
): Promise<WebAuthnCredential> {
  if (resp.clientChallenge !== expectedChallenge) throw new WebAuthnError('challenge mismatch')
  if (resp.origin !== origin) throw new WebAuthnError('origin mismatch')
  if (resp.rpId !== rpId) throw new WebAuthnError('rpId mismatch')

  const result = await verifier.verifyRegistration(resp, expectedChallenge, rpId, origin)
  if (!result.verified) throw new WebAuthnError('attestation not verified')

  return {
    credentialId: result.credentialId,
    accountId,
    publicKey: result.publicKey,
    signCount: result.signCount,
    transports: [...result.transports],
    createdAt: new Date().toISOString(),
  }
}
