import Phaser from 'phaser';
import type { GameState, IncidentRecord, NightState } from '../../data/types';
import {
  buildReport,
  fileReport,
  findKaydenContradiction,
  lieCount,
  type FiledAnswer,
  type ReportQuestion,
} from '../../rules/incidentReport';
import { SeededRNG } from '../../core/SeededRNG';
import { saveGame } from '../../core/save';
import { REPORT_UI } from '../../data/strings/door';
import { Button, DOOR_PALETTE, MONO, panel, text } from './ui';

// The form (design §3.3). You write up what happened, and you may write it up
// generously. Lying is free tonight; the inspector cross-references across
// nights and Phase 4 is where it bites.
//
// Deliberately unscored: nothing here counts your lies back at you, no meter
// moves, and Dazza's line at the end is the same length whatever you picked.
// One night's contradiction is the only feedback, and it arrives a night late.

const W = 640;
const H = 360;

/**
 * Incident details are written for the log, not for the player, so they carry
 * raw patron ids ("admitted p43 in breach"). The id is load-bearing for Phase
 * 4's cross-night audit and stays in the STORED record — it just has no place
 * on a form a person is reading.
 */
function humanise(detail: string): string {
  return detail.replace(/\bp\d+\b/g, 'a patron');
}

/** Wall-clock label for an incident. The night starts at 9 PM (GameClock). */
function clockLabel(clockMin: number): string {
  const total = 21 * 60 + clockMin;
  const h24 = Math.floor(total / 60) % 24;
  const m = total % 60;
  const h12 = h24 % 12 === 0 ? 12 : h24 % 12;
  return `${h12}:${String(m).padStart(2, '0')} ${h24 < 12 ? 'AM' : 'PM'}`;
}

export interface ReportSceneData {
  state: NightState;
  run: GameState | null;
  seed: number;
  /** Where to go once the paperwork is done. */
  next: () => void;
}

export class IncidentReportScene extends Phaser.Scene {
  // NOT `data` — Phaser.Scene already owns that name (its DataManager).
  private input$!: ReportSceneData;
  private questions: ReportQuestion[] = [];
  private answers: FiledAnswer[] = [];
  private index = 0;
  private buttons: Button[] = [];
  private promptText!: Phaser.GameObjects.Text;
  private counter!: Phaser.GameObjects.Text;
  private contradiction: string | undefined;

  constructor() {
    super('IncidentReport');
  }

  init(data: ReportSceneData): void {
    this.input$ = data;
    this.questions = [];
    this.answers = [];
    this.index = 0;
    this.buttons = [];
  }

  create(): void {
    // Its own stream: buildReport's draw count varies with how many incidents
    // the night produced, which is player-driven. Sharing a stream would let
    // door decisions perturb unrelated generation.
    const rng = new SeededRNG(this.input$.seed).stream('report');
    this.questions = buildReport(this.input$.state.incidents, rng);
    // Read BEFORE this night's rows are filed, so tonight cannot contradict itself.
    this.contradiction = this.input$.run
      ? findKaydenContradiction(this.input$.run.pastReports)
      : undefined;

    this.add.rectangle(W / 2, H / 2, W, H, 0x0d0c10);
    this.add
      .text(W / 2, 24, REPORT_UI.title, { fontFamily: MONO, fontSize: '16px', color: '#e8dcc4' })
      .setOrigin(0.5);
    this.add
      .text(W / 2, 44, REPORT_UI.subtitle, { fontFamily: MONO, fontSize: '8px', color: DOOR_PALETTE.inkDim })
      .setOrigin(0.5);

    panel(this, 40, 62, W - 80, 232, 0x15141a, 0x39364a);
    this.counter = text(this, 48, 68, '', 7, DOOR_PALETTE.inkDim);
    this.promptText = this.add.text(48, 84, '', {
      fontFamily: MONO,
      fontSize: '9px',
      color: '#d8d0c0',
      wordWrap: { width: W - 100 },
    });

    if (this.questions.length === 0) {
      // A night with nothing worth writing up is its own small joke.
      this.promptText.setText(REPORT_UI.nothingToReport);
      this.add
        .text(W / 2, H - 20, REPORT_UI.done, { fontFamily: MONO, fontSize: '8px', color: DOOR_PALETTE.inkDim })
        .setOrigin(0.5);
      this.input.once('pointerdown', () => this.finish());
      return;
    }

    this.showQuestion();
  }

  private showQuestion(): void {
    for (const b of this.buttons) b.destroy();
    this.buttons = [];

    const q = this.questions[this.index];
    if (!q) {
      this.finish();
      return;
    }

    this.counter.setText(`incident ${this.index + 1} of ${this.questions.length}`);
    // The bank prompt states the CATEGORY ("a patron was removed"); without the
    // time and the specific detail beside it the player is being asked to
    // account for something the form never names, which is unanswerable rather
    // than morally interesting.
    this.promptText.setText(`${clockLabel(q.incident.clockMin)} — ${humanise(q.incident.detail)}\n${q.prompt}`);

    // Options are laid out in the order buildReport shuffled them into — the
    // truthful one is not always first, and nothing marks which is which.
    let y = 84 + this.promptText.height + 12;
    q.options.forEach((opt) => {
      const label = this.add.text(0, 0, opt.text, {
        fontFamily: MONO,
        fontSize: '8px',
        color: '#cfc6b4',
        wordWrap: { width: W - 132 },
      });
      const h = Math.max(30, label.height + 14);
      label.destroy();

      const b = new Button(this, {
        x: 52,
        y,
        w: W - 104,
        h,
        label: opt.text,
        size: 8,
        fill: 0x231f2c,
        hover: 0x36304a,
        colour: '#cfc6b4',
        onClick: () => this.choose(q, opt),
      });
      b.label.setWordWrapWidth(W - 132);
      b.label.setAlign('left');
      b.setDepth(40);
      this.buttons.push(b);
      y += h + 8;
    });
  }

  private choose(q: ReportQuestion, opt: ReportQuestion['options'][number]): void {
    this.answers.push({ incident: q.incident, chosen: opt });
    this.index++;
    this.showQuestion();
  }

  private finish(): void {
    const filed: IncidentRecord[] = fileReport(this.answers);
    // pastReports is the cross-night memory. Storing the ACCOUNT (not the truth)
    // is what lets a later night catch you out — rules/incidentReport documents
    // the encoding so Phase 4 does not have to reverse-engineer it.
    if (this.input$.run) {
      this.input$.run.pastReports.push(filed);
      // The night's save was written before the paperwork; re-save so the
      // accounts survive into the night that audits them.
      saveGame(this.input$.run);
    }

    const lies = lieCount(this.answers);
    for (const b of this.buttons) b.destroy();
    this.buttons = [];
    this.promptText.setText('');
    this.counter.setText('');

    // Dazza's sign-off is the same shape whether you told the truth or not.
    // He is not the conscience; there isn't one.
    this.add
      .text(W / 2, 150, REPORT_UI.filed, { fontFamily: MONO, fontSize: '11px', color: '#9fe8a0' })
      .setOrigin(0.5);
    const line = this.contradiction ?? REPORT_UI.filedFlavour(lies > 0);
    this.add
      .text(W / 2, 178, line, {
        fontFamily: MONO,
        fontSize: '9px',
        color: this.contradiction ? '#f0d060' : '#bfe8c8',
        align: 'center',
        wordWrap: { width: W - 140 },
      })
      .setOrigin(0.5);
    this.add
      .text(W / 2, H - 20, REPORT_UI.done, { fontFamily: MONO, fontSize: '8px', color: DOOR_PALETTE.inkDim })
      .setOrigin(0.5);

    const go = (): void => this.input$.next();
    this.input.once('pointerdown', go);
    this.input.keyboard?.once('keydown-SPACE', go);
  }
}
