/**
 * lib/canonical-hash.ts — Deterministic content hash for pagespec JSON.
 *
 * Used by `ba-develop/cli/compute-page-diff` (Phase 3a targeted re-runs) to
 * detect whether a pagespec's machine block changed since the last successful
 * run. The hash is stable regardless of object **key order** — re-serialising
 * the same spec with keys in a different order yields the same hash, so a
 * cosmetic reorder by `ba-create-prd` never reads as a false "modified".
 *
 * Array order IS significant (columns / actions / filters carry display order),
 * so `[a, b]` and `[b, a]` intentionally hash differently.
 *
 * Pure: no I/O, no clock, no randomness — same input → same output forever.
 */
import { createHash } from 'node:crypto'

/**
 * Recursively sort object keys and drop `undefined` values so that
 * `JSON.stringify` produces a canonical, order-independent string. Arrays keep
 * their element order; primitives pass through untouched.
 */
export function canonicalize(value: unknown): unknown {
  if (value === null || typeof value !== 'object') return value
  if (Array.isArray(value)) return value.map(canonicalize)
  const sorted: Record<string, unknown> = {}
  for (const key of Object.keys(value as Record<string, unknown>).sort()) {
    const v = (value as Record<string, unknown>)[key]
    if (v !== undefined) sorted[key] = canonicalize(v)
  }
  return sorted
}

/**
 * Deterministic sha256 (base64) of any JSON-serialisable value, prefixed
 * `sha256-` so the algorithm is self-describing in the snapshot file.
 */
export function canonicalHash(value: unknown): string {
  const json = JSON.stringify(canonicalize(value))
  return 'sha256-' + createHash('sha256').update(json).digest('base64')
}
