/**
 * THE SCREEN's base content: procedural "clean programming".
 *
 * A sterile SMPTE-ish test pattern + camcorder overlay, ported from the prototype and widened
 * to 16:9. This is what the factory's output is painted ONTO — bland enough that any corruption
 * reads instantly. Round 5: the SUBSTRATE ages with the highest ratified era (eras.ts); the
 * corruption on top stays era-agnostic.
 *
 * Cost control: the pattern is static, so we only repaint when a visible input changes (timecode
 * ~1/sec, era, chyron text). Motion comes from the shader's slow drift, not from re-uploading
 * 640x360 pixels every frame.
 */
import { eraSkin, type Era, type EraSkin } from './eras';

export const BASE_W = 640;
export const BASE_H = 360;

export interface BaseState {
  timeMs: number;
  era: Era;
  /** chyron line: 'NOTHING IS WRONG' idle era, a radio subtitle when tuned, or '' for none */
  chyron: string;
}

export interface BaseFeed {
  canvas: HTMLCanvasElement;
  /** Repaint if anything visible changed. Returns true when the texture needs re-upload. */
  update(state: BaseState): boolean;
}

function timecode(timeMs: number): string {
  const total = Math.floor(timeMs / 1000) + 754; // 0:12:34
  const h = Math.floor(total / 3600);
  const m = Math.floor((total % 3600) / 60);
  const s = total % 60;
  return `SP ${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
}

function applyTreatment(g: CanvasRenderingContext2D, skin: EraSkin) {
  const t = skin.treatment;
  if (t.kind === 'none') return;
  g.save();
  if (t.kind === 'sepia') {
    // warm the whole frame + gate-weave sprocket holes down both edges
    g.globalCompositeOperation = 'overlay';
    g.fillStyle = 'rgba(150,110,60,0.28)';
    g.fillRect(0, 0, BASE_W, BASE_H);
    g.globalCompositeOperation = 'source-over';
    g.fillStyle = '#0d0904';
    g.fillRect(0, 0, 22, BASE_H);
    g.fillRect(BASE_W - 22, 0, 22, BASE_H);
    g.fillStyle = '#efe2c0';
    for (let y = 6; y < BASE_H; y += 34) {
      g.fillRect(5, y, 12, 18);
      g.fillRect(BASE_W - 17, y, 12, 18);
    }
  } else if (t.kind === 'phosphor') {
    // green cathode wash + heavy interlace lines
    g.globalCompositeOperation = 'multiply';
    g.fillStyle = 'rgba(120,255,150,0.35)';
    g.fillRect(0, 0, BASE_W, BASE_H);
    g.globalCompositeOperation = 'source-over';
    g.fillStyle = 'rgba(0,0,0,0.28)';
    for (let y = 0; y < BASE_H; y += 2) g.fillRect(0, y, BASE_W, 1);
  } else if (t.kind === 'gloss') {
    // rainbow pit-and-land sheen sweeping across, plus a specular band
    const grad = g.createLinearGradient(0, 0, BASE_W, BASE_H);
    grad.addColorStop(0.0, 'rgba(255,0,180,0.10)');
    grad.addColorStop(0.35, 'rgba(0,220,255,0.06)');
    grad.addColorStop(0.7, 'rgba(120,255,120,0.10)');
    grad.addColorStop(1.0, 'rgba(255,180,0,0.08)');
    g.globalCompositeOperation = 'screen';
    g.fillStyle = grad;
    g.fillRect(0, 0, BASE_W, BASE_H);
    g.fillStyle = 'rgba(255,255,255,0.10)';
    g.fillRect(0, BASE_H * 0.2, BASE_W, 26);
    g.globalCompositeOperation = 'source-over';
  } else if (t.kind === 'bleach') {
    // wash toward blinding white, kill contrast
    g.fillStyle = 'rgba(250,250,250,0.6)';
    g.fillRect(0, 0, BASE_W, BASE_H);
  }
  g.restore();
}

export function createBaseFeed(): BaseFeed {
  const canvas = document.createElement('canvas');
  canvas.width = BASE_W;
  canvas.height = BASE_H;
  const g = canvas.getContext('2d')!;
  let last = '';

  function paint(state: BaseState) {
    const skin = eraSkin(state.era);
    g.fillStyle = skin.bg;
    g.fillRect(0, 0, BASE_W, BASE_H);

    const bw = BASE_W / skin.bars.length;
    skin.bars.forEach((col, i) => {
      g.fillStyle = col;
      g.fillRect(i * bw, 0, bw + 1, BASE_H * 0.62);
    });
    skin.casts.forEach((col, i) => {
      g.fillStyle = col;
      g.fillRect(i * bw, BASE_H * 0.62, bw + 1, BASE_H * 0.1);
    });

    // pluge / ramp
    g.fillStyle = skin.bg;
    g.fillRect(0, BASE_H * 0.72, BASE_W, BASE_H * 0.28);
    const ramp = g.createLinearGradient(0, 0, BASE_W, 0);
    ramp.addColorStop(0, '#000');
    ramp.addColorStop(1, '#fff');
    g.fillStyle = ramp;
    g.fillRect(BASE_W * 0.55, BASE_H * 0.8, BASE_W * 0.4, 18);

    // camcorder overlay
    g.fillStyle = skin.ink;
    g.font = "bold 22px 'Courier New', monospace";
    g.fillText('PLAY ▶', 20, BASE_H * 0.86);
    g.fillText(timecode(state.timeMs), 20, BASE_H * 0.95);
    g.font = "bold 18px 'Courier New', monospace";
    g.fillText('WIMVEE FKTRY', BASE_W * 0.55, BASE_H * 0.95);
    g.fillStyle = skin.accent;
    g.beginPath();
    g.arc(BASE_W - 26, BASE_H * 0.83, 7, 0, Math.PI * 2);
    g.fill();

    applyTreatment(g, skin);

    // Chyron band. Carries 'NOTHING IS WRONG' while idle, or a radio subtitle when tuned.
    // Painted AFTER the era treatment so captions stay legible on any substrate.
    // Round-5 review fix (orchestrator): a single un-wrapped fillText showed ~51 chars
    // and 29 of THE STANDARD's 30 lines clipped mid-sentence. "THE SCREEN tells you
    // what it says" has to mean ALL of what it says: word-wrap to two lines, stepping
    // the font down until it fits.
    if (state.chyron) {
      const maxW = BASE_W - 36;
      let lines: string[] = [];
      let px = 20;
      for (const size of [20, 16, 13]) {
        px = size;
        g.font = `bold ${px}px 'Courier New', monospace`;
        lines = [];
        let cur = '';
        for (const word of state.chyron.split(' ')) {
          const next = cur ? cur + ' ' + word : word;
          if (g.measureText(next).width <= maxW) cur = next;
          else { if (cur) lines.push(cur); cur = word; }
        }
        if (cur) lines.push(cur);
        if (lines.length <= 2) break;
      }
      const lineH = px + 6;
      const bandH = 10 + lines.length * lineH;
      g.fillStyle = 'rgba(8,8,12,0.82)';
      g.fillRect(0, BASE_H * 0.5, BASE_W, bandH);
      g.fillStyle = '#d8ffd8';
      lines.forEach((line, i) => g.fillText(line, 18, BASE_H * 0.5 + px + 4 + i * lineH));
    }
  }

  return {
    canvas,
    update(state: BaseState) {
      // Cache key of every visible input; repaint only when one changes.
      const key = `${timecode(state.timeMs)}|${state.era}|${state.chyron}`;
      if (key === last) return false;
      last = key;
      paint(state);
      return true;
    },
  };
}
