/**
 * T15 — CA leaf RENEWAL (INV14). Re-signs a short-TTL leaf for an already-bound, non-revoked host
 * under the current intermediate. SAME guards as T8 `signHostLeaf`: (1) CSR proof-of-possession
 * against the embedded pubkey; (2) embedded pubkey == the host's registered `agent_pubkey`;
 * (3) host active/non-revoked. Any failure → same reject path. A drained/revoked host cannot renew
 * (closes the INV12+INV14 loop). Signing = `CaSigner.sign()` (KMS, §3.1), never a raw key.
 * Distinct file from T8 (`ca/sign.ts`) — shares only the KMS-signer primitive.
 */
import type { HostStore } from '../store/ports.js'
import type { CaSigner } from '../boot/ca-wiring.js'
import { verifyCsrPoP } from './csr.js'
import { LeafSignError } from './sign.js'
import { timingSafeEqualBytes, bytesToBase64 } from '../util/bytes.js'

export interface LeafRenewerDeps {
  readonly hosts: HostStore
  readonly signer: CaSigner
  readonly caChainDer: readonly Uint8Array[]
  readonly leafTtlSec?: number
}

export interface LeafRenewer {
  renewHostLeaf(
    hostId: string,
    csr: Uint8Array,
  ): Promise<{ cert: Uint8Array; caChain: readonly Uint8Array[] }>
}

const DEFAULT_LEAF_TTL_SEC = 24 * 60 * 60

export function createLeafRenewer(deps: LeafRenewerDeps): LeafRenewer {
  const ttl = deps.leafTtlSec ?? DEFAULT_LEAF_TTL_SEC
  return {
    async renewHostLeaf(hostId, csr) {
      const host = await deps.hosts.get(hostId)
      // A revoked/absent host cannot renew (INV12 + INV14).
      if (host === null || host.status === 'revoked') throw new LeafSignError('not_registered')

      const pop = verifyCsrPoP(csr)
      if (!pop.ok) throw new LeafSignError('csr_rejected')
      // embedded pubkey must equal the host's REGISTERED pubkey (no key substitution on renewal).
      if (!timingSafeEqualBytes(pop.embeddedPub, host.agentPubkey)) throw new LeafSignError('csr_rejected')

      const notAfter = Math.floor(Date.now() / 1000) + ttl
      const tbs = new TextEncoder().encode(
        JSON.stringify({ v: 1, hostId, subjectSpki: bytesToBase64(host.agentPubkey), notAfter, renewed: true }),
      )
      const sig = await deps.signer.sign(tbs) // KMS sign(); no raw key
      const cert = new TextEncoder().encode(
        JSON.stringify({ tbs: bytesToBase64(tbs), sig: bytesToBase64(sig) }),
      )
      return { cert, caChain: deps.caChainDer }
    },
  }
}
