/**
 * Pairing-code format (Finding-4). The code is the SOLE credential gating `/enroll`, so it is
 * 26 Crockford-base32 characters = 130 bits of real entropy (NOT the ~32-bit `ABCD-1234` shape,
 * which is rejected). Displayed grouped for humans as 6 dash-separated groups; a paste/QR-first
 * credential, not a type-from-memory PIN.
 */
import { randomBytes } from 'node:crypto'

/** Crockford base32 alphabet (excludes I L O U to avoid confusion). */
const CROCKFORD = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
export const PAIRING_CODE_LEN = 26 // 26 * 5 bits = 130 bits
export const PAIRING_CODE_BITS = PAIRING_CODE_LEN * 5

/** Generate a canonical (undashed, uppercase) 26-char Crockford code. */
export function generatePairingCode(): string {
  // Draw enough random bytes, map 5 bits per char.
  const bytes = randomBytes(PAIRING_CODE_LEN)
  let out = ''
  for (let i = 0; i < PAIRING_CODE_LEN; i++) {
    out += CROCKFORD[(bytes[i] as number) & 0x1f]
  }
  return out
}

/** Group the canonical code for display: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-X. */
export function formatPairingCode(canonical: string): string {
  return (canonical.match(/.{1,5}/g) ?? []).join('-')
}

/**
 * Normalize a user-presented code back to canonical form: uppercase, strip dashes/spaces, map
 * Crockford-confusable characters (I/L→1, O→0). Redemption hashes THIS canonical form.
 */
export function normalizePairingCode(raw: string): string {
  return raw
    .toUpperCase()
    .replace(/[\s-]/g, '')
    .replace(/[IL]/g, '1')
    .replace(/O/g, '0')
    .replace(/U/g, 'V')
}
