import Phaser from 'phaser';
import type { RngStream } from '../../core/SeededRNG';
import { challengesFor, perform, type Challenge, type Performance } from '../../rules/sobriety';
import type { Patron } from '../../data/types';
import { SOBRIETY_UI } from '../../data/strings/door';
import { Button, DOOR_PALETTE, MONO } from './ui';

// The power trip, in modal form. They perform, you rule. The ruling is not
// required to agree with the performance — that gap is the whole point, so the
// PASS/FAIL buttons are deliberately identical in weight.

export type SobrietyRuling = 'pass' | 'fail';

export interface SobrietyResult {
  ruling: SobrietyRuling;
  performance: Performance;
}

const TYPE_MS_PER_CHAR = 42;

export class SobrietyModal {
  private root: Phaser.GameObjects.Container | null = null;
  private readonly buttons: Button[] = [];
  private typeEvent: Phaser.Time.TimerEvent | null = null;

  constructor(private readonly scene: Phaser.Scene) {}

  get isOpen(): boolean {
    return this.root !== null;
  }

  open(patron: Patron, rng: RngStream, onResult: (r: SobrietyResult) => void): void {
    this.close();
    const { scene } = this;
    const root = scene.add.container(320, 180).setDepth(760);
    this.root = root;

    // Interactive so it EATS clicks: without this the ADMIT / NOT TONIGHT
    // buttons underneath stay live and you can rule on someone mid-test.
    root.add(scene.add.rectangle(0, 0, 1280, 720, 0x05050a, 0.7).setInteractive());
    root.add(scene.add.rectangle(0, 0, 340, 210, 0x14121a).setStrokeStyle(1, 0x5a4a68));
    root.add(
      scene.add
        .text(0, -92, SOBRIETY_UI.title, { fontFamily: MONO, fontSize: '11px', color: '#f0e4d0' })
        .setOrigin(0.5),
    );

    const prompt = scene.add
      .text(0, -68, SOBRIETY_UI.pickPrompt, { fontFamily: MONO, fontSize: '8px', color: '#9a8fa8' })
      .setOrigin(0.5);
    root.add(prompt);

    const options = challengesFor(rng);
    options.forEach((c, i) => {
      const b = new Button(scene, {
        x: 320 - 150 + i * 152,
        y: 180 - 44,
        w: 146,
        h: 40,
        label: c.kind === 'alphabet' ? 'ALPHABET\nBACKWARDS' : 'ANSWER ME\nTHIS',
        size: 8,
        fill: DOOR_PALETTE.deskEdge,
        onClick: () => this.runChallenge(patron, c, rng, prompt, onResult),
      });
      b.setDepth(770);
      b.label.setAlign('center');
      this.buttons.push(b);
    });

    root.add(
      scene.add
        .text(0, 88, SOBRIETY_UI.hint, { fontFamily: MONO, fontSize: '7px', color: '#5a5068' })
        .setOrigin(0.5),
    );
  }

  private runChallenge(
    patron: Patron,
    challenge: Challenge,
    rng: RngStream,
    prompt: Phaser.GameObjects.Text,
    onResult: (r: SobrietyResult) => void,
  ): void {
    const { scene } = this;
    const root = this.root;
    if (!root) return;

    this.clearButtons();
    prompt.setText(`"${challenge.prompt}"`).setColor('#d8d0c0').setWordWrapWidth(300);

    const performance = perform(patron, challenge, rng);

    const answer = scene.add
      .text(0, -14, '', {
        fontFamily: MONO,
        fontSize: '10px',
        color: '#9fe8a0',
        align: 'center',
        wordWrap: { width: 300 },
      })
      .setOrigin(0.5, 0);
    root.add(answer);

    // Typed out at drunk speed. Watching them struggle IS the information.
    let i = 0;
    this.typeEvent = scene.time.addEvent({
      delay: TYPE_MS_PER_CHAR,
      repeat: performance.answer.length - 1,
      callback: () => {
        i++;
        answer.setText(performance.answer.slice(0, i));
        if (i >= performance.answer.length) this.showRuling(performance, onResult);
      },
    });
    if (performance.answer.length === 0) this.showRuling(performance, onResult);
  }

  private showRuling(performance: Performance, onResult: (r: SobrietyResult) => void): void {
    const { scene } = this;
    if (!this.root) return;

    const finish = (ruling: SobrietyRuling): void => {
      this.close();
      onResult({ ruling, performance });
    };

    // Same size, same colour weight. The game does not tell you which is correct.
    const pass = new Button(scene, {
      x: 320 - 150,
      y: 180 + 40,
      w: 146,
      h: 30,
      label: SOBRIETY_UI.passBtn,
      fill: DOOR_PALETTE.green,
      hover: DOOR_PALETTE.greenHot,
      onClick: () => finish('pass'),
    });
    const fail = new Button(scene, {
      x: 320 + 4,
      y: 180 + 40,
      w: 146,
      h: 30,
      label: SOBRIETY_UI.failBtn,
      fill: DOOR_PALETTE.red,
      hover: DOOR_PALETTE.redHot,
      onClick: () => finish('fail'),
    });
    pass.setDepth(770);
    fail.setDepth(770);
    this.buttons.push(pass, fail);
  }

  private clearButtons(): void {
    for (const b of this.buttons) b.destroy();
    this.buttons.length = 0;
  }

  close(): void {
    this.typeEvent?.remove();
    this.typeEvent = null;
    this.clearButtons();
    this.root?.destroy();
    this.root = null;
  }
}
