/**
 * T7 — pairing-code issuance (INDEX §4.5 step 1). Mints a single-use short-TTL code; stores
 * `{ code_hash, account_id, expires_at, redeem_attempts:0 }` — the RAW code is never stored
 * (INV5). `accountId` comes from the authenticated principal (INV3), never a request field.
 */
import type { PairingCodeRecord } from '../model/records.js'
import type { PairingStore } from '../store/ports.js'
import type { AuditWriter } from '../audit/log.js'
import { noopAuditWriter } from '../audit/log.js'
import { generatePairingCode, formatPairingCode } from './code.js'
import { sha256Hex } from '../util/hash.js'
import { nowIso } from '../util/ids.js'

export interface IssuedPairing {
  readonly code: string // display-grouped, returned ONCE
  readonly expiresAt: string
}

export interface PairingIssuer {
  issuePairingCode(accountId: string): Promise<IssuedPairing>
}

export interface PairingIssuerDeps {
  readonly pairing: PairingStore
  readonly pairingTtlSec: number
  readonly audit?: AuditWriter
  readonly actor?: string
}

export function createPairingIssuer(deps: PairingIssuerDeps): PairingIssuer {
  const audit = deps.audit ?? noopAuditWriter()
  const actor = deps.actor ?? 'system'
  return {
    async issuePairingCode(accountId) {
      const canonical = generatePairingCode()
      const codeHash = sha256Hex(canonical) // deterministic hash of a 130-bit input (INV5)
      const expiresAt = new Date(Date.now() + deps.pairingTtlSec * 1000).toISOString()
      const record: PairingCodeRecord = { codeHash, accountId, expiresAt, redeemedAt: null }
      await deps.pairing.insert(record)
      await audit.writeAuditEvent({
        action: 'pairing.issue',
        principalId: actor,
        accountId,
        hostId: null,
        ts: nowIso(),
        meta: { expiresAt },
      })
      // Raw code returned ONCE, display-grouped; never persisted.
      return { code: formatPairingCode(canonical), expiresAt }
    },
  }
}
