import Phaser from 'phaser';
import type { EventBus } from '../../core/EventBus';
import { DOOR_PALETTE, MONO, text } from './ui';

// Dazza is never seen (design §4.4) — he is entirely this widget. Buzz, preview,
// and a thread you can scroll back through when you have forgotten which
// increasingly unhinged rule is currently law.

/** Lines of preview that fit inside the 56x32 phone screen at 6px. */
const PREVIEW_MAX_LINES = 3;

export interface PhoneMessage {
  clockLabel: string;
  body: string;
  isRule: boolean;
}

export class PhoneWidget {
  private readonly root: Phaser.GameObjects.Container;
  private readonly screen: Phaser.GameObjects.Rectangle;
  private readonly preview: Phaser.GameObjects.Text;
  private readonly badge: Phaser.GameObjects.Container;
  private readonly badgeCount: Phaser.GameObjects.Text;
  private thread: Phaser.GameObjects.Container | null = null;
  private readonly messages: PhoneMessage[] = [];
  private unread = 0;
  private readonly offs: Array<() => void> = [];

  constructor(
    private readonly scene: Phaser.Scene,
    bus: EventBus,
    x: number,
    y: number,
    private readonly onOpened: () => void,
    private readonly clockLabel: () => string = () => '',
  ) {
    this.root = scene.add.container(x, y).setDepth(60);

    const body = scene.add.rectangle(0, 0, 64, 46, 0x101014).setStrokeStyle(1, 0x3a3a44);
    this.screen = scene.add.rectangle(0, -3, 56, 32, 0x0d1a14);
    this.preview = scene.add
      .text(-26, -17, '', {
        fontFamily: MONO,
        fontSize: '6px',
        color: '#7de08a',
        wordWrap: { width: 52 },
        // A phone screen is 56x32. Without a hard cap a long rule text wraps to
        // six lines and spills green out onto the desk timber.
        maxLines: PREVIEW_MAX_LINES,
      })
      .setOrigin(0, 0);
    const home = scene.add.rectangle(0, 17, 14, 3, 0x3a3a44);
    const caption = text(scene, -26, -25, 'DAZZA', 6, '#4a8a58');

    this.badge = scene.add.container(26, -20);
    this.badge.add(scene.add.circle(0, 0, 6, DOOR_PALETTE.red));
    this.badgeCount = scene.add
      .text(0, 0, '1', { fontFamily: MONO, fontSize: '7px', color: '#ffffff' })
      .setOrigin(0.5);
    this.badge.add(this.badgeCount);
    this.badge.setVisible(false);

    this.root.add([body, this.screen, caption, this.preview, home, this.badge]);

    body.setInteractive({ useHandCursor: true }).on('pointerdown', () => this.toggleThread());
    this.screen.setInteractive({ useHandCursor: true }).on('pointerdown', () => this.toggleThread());

    this.offs.push(
      bus.on('dazza:text', ({ text: line, rule }) => this.receive(line, !!rule)),
    );
  }

  private receive(body: string, isRule: boolean): void {
    this.messages.push({ clockLabel: this.clockLabel(), body, isRule });
    this.unread++;
    this.badge.setVisible(true);
    this.badgeCount.setText(String(Math.min(9, this.unread)));
    this.preview.setText(body.length > 44 ? `${body.slice(0, 42)}…` : body);
    this.buzz();
  }

  private buzz(): void {
    const { scene } = this;
    scene.tweens.add({
      targets: this.root,
      x: this.root.x + 2,
      duration: 45,
      yoyo: true,
      repeat: 5,
      ease: 'Sine.easeInOut',
    });
    scene.tweens.add({
      targets: this.screen,
      fillAlpha: { from: 1, to: 0.55 },
      duration: 110,
      yoyo: true,
      repeat: 3,
    });
  }

  private toggleThread(): void {
    if (this.thread) {
      this.closeThread();
      return;
    }
    this.onOpened();
    this.unread = 0;
    this.badge.setVisible(false);

    const { scene } = this;
    const c = scene.add.container(320, 180).setDepth(720);
    this.thread = c;

    c.add(
      scene.add
        .rectangle(0, 0, 1280, 720, 0x05050a, 0.6)
        .setInteractive()
        .on('pointerdown', () => this.closeThread()),
    );
    c.add(scene.add.rectangle(0, 0, 300, 300, 0x0d1116).setStrokeStyle(1, 0x2c4a38));
    c.add(
      scene.add
        .text(0, -134, 'DAZZA (MANAGEMENT)', { fontFamily: MONO, fontSize: '9px', color: '#7de08a' })
        .setOrigin(0.5),
    );

    // Newest at the bottom, like a real thread; older ones scroll off the top.
    const shown = this.messages.slice(-8);
    let y = -112;
    if (shown.length === 0) {
      c.add(
        scene.add
          .text(0, 0, 'no messages yet\nenjoy it while it lasts', {
            fontFamily: MONO,
            fontSize: '8px',
            color: '#4a6a58',
            align: 'center',
          })
          .setOrigin(0.5),
      );
    }
    for (const m of shown) {
      const bubble = scene.add
        .text(-134, y, m.body, {
          fontFamily: MONO,
          fontSize: '8px',
          color: m.isRule ? '#f0d060' : '#bfe8c8',
          wordWrap: { width: 262 },
        })
        .setOrigin(0, 0);
      c.add(bubble);
      if (m.clockLabel) {
        c.add(
          scene.add
            .text(134, y, m.clockLabel, { fontFamily: MONO, fontSize: '6px', color: '#3d5a48' })
            .setOrigin(1, 0),
        );
      }
      y += bubble.height + 8;
    }
    c.add(
      scene.add
        .text(0, 136, 'click anywhere to pocket it', { fontFamily: MONO, fontSize: '7px', color: '#3d5a48' })
        .setOrigin(0.5),
    );
  }

  private closeThread(): void {
    this.thread?.destroy();
    this.thread = null;
  }

  get isThreadOpen(): boolean {
    return this.thread !== null;
  }

  destroy(): void {
    for (const off of this.offs) off();
    this.closeThread();
    this.root.destroy();
  }
}
