// dimensions.mjs — single source of truth for the video canvas size.
//
// ============================================================================
//  THE A/B ↔ C SEAM  (read this before wiring orientation in upstream)
// ============================================================================
// The whole faceless-explainer pipeline used to be hard-locked to landscape
// 1920×1080. It is now dimension-parametric: every deterministic script
// (assemble-index, transitions, captions, preflight-finalize) and every
// scene worker reads the canvas size from ONE place — `group_spec.json`
// `width`/`height` — which prep.mjs stamps in.
//
// prep.mjs resolves the size with this precedence (see resolveDimensions):
//   1. explicit `--width N --height N` flags on prep        (testing / override)
//   2. `narrator_scripts.json` → `dimensions: { width, height }`   (explicit px)
//   3. `narrator_scripts.json` → `orientation: "landscape|portrait|square"`
//   4. default → landscape 1920×1080                        (back-compat)
//
// >>> For whoever wires the intent→orientation entry (Step 0 / scriptwriting):
//     do NOT thread pixels through every script. Just write ONE field into
//     `narrator_scripts.json` — `orientation` (friendly) or `dimensions`
//     (explicit px) — exactly the way `stylePreset` already lives there. prep
//     picks it up and the rest of the pipeline follows automatically. If you
//     write nothing, the video stays landscape (no behavior change).
// ============================================================================

// Named orientation presets. Square/portrait are 1080-based so they share the
// long-edge pixel budget with landscape (same render cost ballpark).
export const ORIENTATION_PRESETS = {
  landscape: { width: 1920, height: 1080 }, // 16:9 — default
  portrait: { width: 1080, height: 1920 }, // 9:16 — reels / shorts / TikTok
  square: { width: 1080, height: 1080 }, // 1:1 — feed
};

export const DEFAULT_DIMENSIONS = ORIENTATION_PRESETS.landscape;

function sane(w, h) {
  return Number.isFinite(w) && Number.isFinite(h) && w >= 240 && h >= 240 && w <= 8192 && h <= 8192;
}

// Resolve canvas dims from (in priority order) explicit flags, an explicit
// `dimensions` object, a named `orientation`, else the landscape default.
// `flags` = { width, height } (strings or numbers, may be undefined).
// `narratorScripts` = the parsed narrator_scripts.json (may be null).
// Returns { width, height, source } — `source` is for logging/anomalies.
export function resolveDimensions(flags = {}, narratorScripts = null) {
  const fw = flags.width != null ? parseInt(flags.width, 10) : NaN;
  const fh = flags.height != null ? parseInt(flags.height, 10) : NaN;
  if (sane(fw, fh)) return { width: fw, height: fh, source: "flags" };

  const dim = narratorScripts && narratorScripts.dimensions;
  if (dim) {
    const w = parseInt(dim.width, 10);
    const h = parseInt(dim.height, 10);
    if (sane(w, h)) return { width: w, height: h, source: "narrator_scripts.dimensions" };
  }

  const orient =
    narratorScripts && typeof narratorScripts.orientation === "string"
      ? narratorScripts.orientation.trim().toLowerCase()
      : "";
  if (orient && ORIENTATION_PRESETS[orient]) {
    return { ...ORIENTATION_PRESETS[orient], source: `orientation=${orient}` };
  }

  return { ...DEFAULT_DIMENSIONS, source: "default(landscape)" };
}

// Read dims back out of a group_spec (downstream consumers). Falls back to the
// landscape default so a group_spec produced before this change still works.
export function readDims(groupSpec) {
  const w = parseInt(groupSpec?.width, 10);
  const h = parseInt(groupSpec?.height, 10);
  if (sane(w, h)) return { width: w, height: h };
  return { ...DEFAULT_DIMENSIONS };
}

// Caption band geometry, derived from canvas height. The band is the bottom
// ~16.67% of the canvas (matches the original landscape 180px band at h=1080:
// 1080 − round(1080 × 0.1667) = 900). Foreground content must end `safetyPx`
// above the band top.
export const CAPTION_BAND_FRACTION = 0.1667;
export function captionBand(height, safetyPx = 20) {
  const h = Number.isFinite(height) ? height : DEFAULT_DIMENSIONS.height;
  const bandHeight = Math.round(h * CAPTION_BAND_FRACTION);
  const bandTopY = h - bandHeight; // foreground must end at/above this y
  return { bandHeight, bandTopY, foregroundMaxY: bandTopY - safetyPx };
}
