import Phaser from 'phaser';
import { renderDoll } from '../../patrons/doll';
import { idVerdict, idDisplayLines, formatDate } from '../../rules/idCheck';
import type { Patron } from '../../data/types';
import { DOOR_PALETTE, MONO, text } from './ui';

// The ID card, at two sizes: a thumbnail sitting in the tray and a half-screen
// zoom you actually read. Every fake tell is drawn, never labelled — the player
// judges pixels, rules/idCheck judges the data (design §4.1).

const CARD_W = 268;
const CARD_H = 156;

/** The genuine hologram colour. The desk carries a reference swatch of this so a
 *  wrong hologram is a fair catch rather than a memory test. */
export const GENUINE_HOLOGRAM = 0x46c8c0;
const FAKE_HOLOGRAM = 0xc046b0;

export class IdCardView {
  private root: Phaser.GameObjects.Container | null = null;
  private patron: Patron | null = null;

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

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

  /** Small always-visible representation for the tray. Caller owns placement. */
  thumbnail(p: Patron, x: number, y: number): Phaser.GameObjects.Container {
    const c = this.scene.add.container(x, y);
    const bg = this.scene.add.rectangle(0, 0, 78, 46, 0xdad4c4).setStrokeStyle(1, 0x8b8474);
    const photo = this.scene.add.image(-24, 0, renderDoll(this.scene, p, 'idPhoto')).setScale(1.4);
    const holo = this.scene.add
      .rectangle(22, -14, 26, 8, p.idCard.fake?.wrongHologram ? FAKE_HOLOGRAM : GENUINE_HOLOGRAM, 0.75)
      .setAngle(-8);
    const name = this.scene.add
      .text(-6, -4, p.idCard.name.slice(0, 13), { fontFamily: MONO, fontSize: '7px', color: '#2a2620' })
      .setOrigin(0, 0.5);
    c.add([bg, photo, holo, name]);
    if (p.idCard.fake?.peelingLaminate) {
      c.add(this.scene.add.triangle(36, 20, 0, 0, 9, 0, 9, -9, 0xf4f0e6, 0.85));
    }
    c.setSize(78, 46);
    return c;
  }

  open(p: Patron): void {
    if (this.root) this.close();
    this.patron = p;
    const { scene } = this;
    // Parked right of centre on purpose: the photo-mismatch tell is only
    // playable if you can see the card and the actual face at the same time, so
    // the card must never sit on top of the patron at the rope (x ~252..348).
    const cx = 462;
    const cy = 134;

    const root = scene.add.container(cx, cy).setDepth(700);
    this.root = root;

    // Dim the street enough to focus the card, light enough to still read a face.
    const shade = scene.add
      .rectangle(-cx, -cy, 1280, 720, 0x05050a, 0.4)
      .setOrigin(0, 0)
      .setInteractive()
      .on('pointerdown', () => this.close());
    root.add(shade);

    const card = scene.add.rectangle(0, 0, CARD_W, CARD_H, 0xdad4c4).setStrokeStyle(2, 0x8b8474);
    root.add(card);

    // state banner
    root.add(scene.add.rectangle(0, -CARD_H / 2 + 13, CARD_W - 8, 20, 0x2a4a8a));
    root.add(
      scene.add
        .text(0, -CARD_H / 2 + 13, 'QUEENSLAND · ADULT PROOF OF AGE', {
          fontFamily: MONO,
          fontSize: '9px',
          color: '#dfe6f4',
        })
        .setOrigin(0.5),
    );

    // photo, rendered from photoSeed — a mismatch is a visibly different person
    const photoKey = renderDoll(scene, p, 'idPhoto');
    root.add(scene.add.rectangle(-84, 8, 84, 84, 0xb8b2a2));
    root.add(scene.add.image(-84, 8, photoKey).setScale(3.2));

    const lines = idDisplayLines(p.idCard, this.nightDate);
    const field = (label: string, value: string, y: number, big = false): void => {
      root.add(
        scene.add
          .text(-26, y, label, { fontFamily: MONO, fontSize: '7px', color: '#6b6558' })
          .setOrigin(0, 0.5),
      );
      root.add(
        scene.add
          .text(-26, y + 11, value, {
            fontFamily: MONO,
            fontSize: big ? '13px' : '11px',
            color: '#1e1a14',
          })
          .setOrigin(0, 0.5),
      );
    };
    field('NAME', lines.name, -36, true);
    field('DATE OF BIRTH', lines.dob, 2, true);
    field('EXPIRES', lines.expiry, 40);

    // The whole ID minigame is one subtraction, and the player cannot do it
    // without the other operand. Printed on the card itself rather than only on
    // the desk so both numbers are in the same glance.
    root.add(
      scene.add
        .text(92, 4, `TONIGHT\n${formatDate(this.nightDate)}`, {
          fontFamily: MONO,
          fontSize: '8px',
          color: '#8a3a3a',
          align: 'center',
          lineSpacing: 2,
        })
        .setOrigin(0.5)
        .setAngle(-4),
    );

    // hologram over the corner — wrong colour is the tell, the desk has a swatch
    const holoColour = p.idCard.fake?.wrongHologram ? FAKE_HOLOGRAM : GENUINE_HOLOGRAM;
    const holo = scene.add.rectangle(92, 44, 58, 22, holoColour, 0.55).setAngle(-10);
    root.add(holo);
    root.add(scene.add.rectangle(92, 44, 58, 3, 0xffffff, 0.3).setAngle(-10));
    scene.tweens.add({ targets: holo, alpha: { from: 0.35, to: 0.7 }, duration: 900, yoyo: true, repeat: -1 });

    if (p.idCard.fake?.peelingLaminate) {
      // lifted corner + a couple of detached flecks
      root.add(scene.add.triangle(CARD_W / 2 - 22, CARD_H / 2 - 22, 0, 0, 30, 0, 30, -30, 0xf6f2e8, 0.9));
      root.add(scene.add.rectangle(CARD_W / 2 - 40, CARD_H / 2 - 8, 5, 3, 0xf6f2e8, 0.8));
      root.add(scene.add.rectangle(CARD_W / 2 - 52, CARD_H / 2 - 14, 3, 3, 0xf6f2e8, 0.7));
    }

    const v = idVerdict(p.idCard, this.nightDate);
    if (v.expired) {
      root.add(
        scene.add
          .text(-40, 62, 'EXPIRED', { fontFamily: MONO, fontSize: '10px', color: '#a02020' })
          .setOrigin(0, 0.5)
          .setAngle(-6),
      );
    }

    root.add(
      text(scene, -CARD_W / 2 + 6, CARD_H / 2 - 12, 'click anywhere to put it down', 7, '#6b6558'),
    );

    root.setScale(0.2);
    root.setAlpha(0);
    scene.tweens.add({ targets: root, scale: 1, alpha: 1, duration: 150, ease: 'Back.easeOut' });
  }

  close(): void {
    this.root?.destroy();
    this.root = null;
    this.patron = null;
  }

  /**
   * Reference swatch for the desk: what a real hologram looks like. It sits on
   * the SAME cream backing the card uses — a translucent teal reads completely
   * differently over dark laminate than over card stock, and a reference the
   * player can't trust is worse than no reference.
   */
  static holoSwatch(scene: Phaser.Scene, x: number, y: number): Phaser.GameObjects.Container {
    const c = scene.add.container(x, y);
    c.add(scene.add.rectangle(0, 0, 38, 15, 0xdad4c4));
    c.add(scene.add.rectangle(0, 0, 30, 11, GENUINE_HOLOGRAM, 0.55));
    c.add(scene.add.rectangle(0, 0, 30, 3, 0xffffff, 0.3));
    c.add(
      scene.add
        .text(0, 10, 'GENUINE', { fontFamily: MONO, fontSize: '6px', color: DOOR_PALETTE.inkDim })
        .setOrigin(0.5, 0),
    );
    return c;
  }

  get currentPatron(): Patron | null {
    return this.patron;
  }
}
