import * as THREE from 'three';
import type { App, View } from '../core/app';
import type { Slice } from '../sim/slice';
import type { Order } from '../game/orders';
import type { Verdict } from '../game/judging';
import { judgeFace, verdictLines } from '../game/lines';
import { TOOLS, type ToolId } from '../sim/cutlery';
import { clear, el, Panel } from '../ui/hud';

/**
 * The scorecard. This is the screen the game is actually about: every number
 * points at something the player did, and the toast turns slowly under a light
 * so they can see it for themselves.
 */
export class JudgeView implements View {
  readonly root = new THREE.Group();
  private pedestal: THREE.Group;
  private slice: Slice | null = null;
  private spin = 0;
  private heatmap: 0 | 1 | 2 = 0;

  private panel: Panel;
  private cardEl!: HTMLElement;
  private linesEl!: HTMLElement;
  private stampEl!: HTMLElement;
  private faceEl!: HTMLImageElement;
  private orderEl!: HTMLElement;
  private nextBtn!: HTMLButtonElement;
  private viewBtn!: HTMLButtonElement;

  onNext: (() => void) | null = null;

  constructor(private app: App) {
    this.pedestal = new THREE.Group();
    const top = new THREE.Mesh(
      new THREE.CylinderGeometry(1.05, 1.05, 0.12, 48),
      new THREE.MeshStandardMaterial({ color: 0x2a2320, roughness: 0.55 }),
    );
    top.receiveShadow = true;
    this.pedestal.add(top);
    const stem = new THREE.Mesh(
      new THREE.CylinderGeometry(0.5, 0.72, 0.9, 32),
      new THREE.MeshStandardMaterial({ color: 0x1d1815, roughness: 0.7 }),
    );
    stem.position.y = -0.5;
    this.pedestal.add(stem);
    this.pedestal.position.set(0, 0.9, 0);
    this.root.add(this.pedestal);

    const spot = new THREE.SpotLight(0xfff0d8, 90, 12, 0.5, 0.55, 1.6);
    spot.position.set(1.4, 4.2, 2.2);
    spot.target = this.pedestal;
    spot.castShadow = true;
    spot.shadow.mapSize.set(1024, 1024);
    this.root.add(spot);
    const rim = new THREE.SpotLight(0x88a8ff, 40, 12, 0.6, 0.7, 1.6);
    rim.position.set(-2.6, 2.4, -2.2);
    rim.target = this.pedestal;
    this.root.add(rim);

    this.panel = new Panel('judge-panel');
    this.buildUi();
    this.panel.hide();
  }

  private buildUi(): void {
    const p = this.panel.root;
    const left = el('div', 'judge-left', p);
    this.faceEl = el('img', 'judge-face', left);
    this.faceEl.alt = '';
    this.linesEl = el('div', 'judge-lines', left);

    const right = el('div', 'judge-right', p);
    this.orderEl = el('div', 'judge-order', right);
    this.cardEl = el('div', 'judge-card', right);
    const foot = el('div', 'judge-foot', right);
    this.stampEl = el('div', 'judge-stamp', foot);
    const btns = el('div', 'judge-btns', foot);
    this.viewBtn = el('button', 'btn ghost', btns, 'HEATMAP');
    this.viewBtn.addEventListener('click', () => this.cycleHeatmap());
    this.nextBtn = el('button', 'btn', btns, 'NEXT ORDER');
    this.nextBtn.addEventListener('click', () => this.onNext?.());
  }

  private cycleHeatmap(): void {
    this.heatmap = ((this.heatmap + 1) % 3) as 0 | 1 | 2;
    this.slice?.setHeatmap(this.heatmap);
    this.viewBtn.textContent = ['HEATMAP', 'BROWNING', 'SPREAD'][this.heatmap];
  }

  show(slice: Slice, verdict: Verdict, order: Order, tool: ToolId, rand: () => number): void {
    this.slice = slice;
    this.spin = 0;
    this.heatmap = 0;
    slice.setHeatmap(0);
    slice.setPresentation(true);
    this.viewBtn.textContent = 'HEATMAP';

    // Take the toast off the bench and put it under the light.
    this.pedestal.add(slice.mesh);
    slice.mesh.position.set(0, 0.06 + slice.halfThickness, 0);
    slice.mesh.rotation.set(0, 0, 0);
    slice.mesh.scale.setScalar(1.55);

    this.orderEl.textContent = `Day ${order.day} · ${order.who} asked for ${order.browningName}, ${order.amount} ${order.spread === 'mitey' ? 'MITEY' : order.spread}`;

    clear(this.cardEl);
    for (const c of verdict.criteria) {
      const row = el('div', 'crit', this.cardEl);
      el('span', 'crit-name', row, c.label);
      const bar = el('span', 'crit-bar', row);
      const fill = el('span', 'crit-fill', bar);
      fill.style.width = `${Math.round(c.score * 100)}%`;
      fill.style.background = c.score > 0.8 ? '#6cbf6c' : c.score > 0.5 ? '#e8a53a' : '#c0392b';
      el('span', 'crit-detail', row, c.detail);
    }
    const totalRow = el('div', 'crit total', this.cardEl);
    el('span', 'crit-name', totalRow, 'TOTAL');
    el('span', 'crit-bar', totalRow);
    el('span', 'crit-detail', totalRow, `${verdict.total.toFixed(1)} / 10`);

    clear(this.linesEl);
    const lines = verdictLines(verdict, order, TOOLS[tool].name, rand);
    for (const l of lines) el('p', undefined, this.linesEl, l);

    this.faceEl.src = `/assets/img/judge_${judgeFace(verdict.grade)}.png`;
    this.stampEl.textContent = verdict.grade;
    this.stampEl.className = `judge-stamp grade-${verdict.grade}`;
    // re-trigger the stamp animation
    this.stampEl.style.animation = 'none';
    void this.stampEl.offsetHeight;
    this.stampEl.style.animation = '';
  }

  /** Hand the toast back so the kitchen can bin it. */
  release(): Slice | null {
    const s = this.slice;
    if (s) {
      s.mesh.scale.setScalar(1);
      s.setHeatmap(0);
      s.setPresentation(false);
      this.pedestal.remove(s.mesh);
    }
    this.slice = null;
    return s;
  }

  enter(): void {
    this.panel.show();
    this.app.camera.position.set(0.15, 2.35, 3.5);
    this.app.camera.lookAt(0, 1.15, 0);
    this.app.shake = 0.05;
  }

  exit(): void {
    this.panel.hide();
  }

  update(dt: number): void {
    this.spin += dt * 0.42;
    if (this.slice) this.slice.mesh.rotation.y = this.spin;
    this.app.camera.position.set(0.15, 2.35, 3.5);
    this.app.camera.lookAt(0, 1.15, 0);
  }

  dispose(): void {
    this.panel.dispose();
  }
}
