import type { Stage } from '../scenes/stage';
import { Samurai } from '../sim/samurai';
import { judge, type Order, type RunResult, type Verdict } from './judging';

/** A day introduces exactly ONE new pressure (never two) and stays beatable by
 *  a scripted-but-honest run. angerRamp scales every sin — the samurai's
 *  patience shortens on the rush-hour day. */
export interface Day {
  order: Order;
  pressure: string;
  angerRamp: number;
}

export const DAYS: Day[] = [
  { order: { name: 'Open Tomato Sando', layers: ['bread', 'tomato', 'bread'], needsCut: true, maxSeconds: 45 }, pressure: 'the basics', angerRamp: 1 },
  { order: { name: 'Cheese & Tomato', layers: ['bread', 'cheese', 'tomato', 'bread'], needsCut: true, maxSeconds: 45 }, pressure: '+1 ingredient', angerRamp: 1 },
  { order: { name: 'Double Tomato', layers: ['bread', 'tomato', 'tomato', 'bread'], needsCut: true, maxSeconds: 45 }, pressure: 'wetter — twice the tomato', angerRamp: 1 },
  { order: { name: 'Katsu Sando', layers: ['bread', 'katsu', 'tomato', 'bread'], needsCut: true, maxSeconds: 45 }, pressure: 'a real cut — the katsu', angerRamp: 1 },
  { order: { name: 'Rush Hour', layers: ['bread', 'tomato', 'bread'], needsCut: true, maxSeconds: 22 }, pressure: 'the patience ramp', angerRamp: 1.8 },
];

export const ORDERS: Order[] = DAYS.map((d) => d.order);

/**
 * The run controller. Holds the samurai + the current ticket, tallies the floor,
 * and turns a finished attempt into a Verdict. Kitchen sins feed the anger
 * meter; the meter topping out fires the string cut (wired to the stage).
 */
export class Game {
  samurai = new Samurai();
  day = 0;
  order: Order = DAYS[0].order;
  drops = 0;
  seconds = 0; // ticket timer (seconds)

  constructor(private stage: Stage) {
    this.samurai.onStrike = () => this.stage.stringCut();
  }

  /** Load day `n` (clamped): its order + its samurai patience ramp. */
  setDay(n: number): Day {
    this.day = Math.max(0, Math.min(DAYS.length - 1, n));
    const d = DAYS[this.day];
    this.order = d.order;
    this.samurai.ramp = d.angerRamp;
    this.drops = 0;
    this.seconds = 0;
    return d;
  }

  reset(order: Order = this.order): void {
    this.samurai.reset();
    this.order = order;
    this.drops = 0;
    this.seconds = 0;
  }

  tick(dtSec: number): void {
    this.seconds += dtSec;
  }

  // ---- kitchen sins → anger ----
  drop(): void {
    this.drops++;
    this.samurai.bump('drop');
  }
  splat(): void {
    this.samurai.bump('splat');
  }
  crimeCut(): void {
    this.samurai.bump('crimeCut');
  }
  offstage(): void {
    this.samurai.bump('offstage');
  }

  private buildRun(bowHeld: boolean, seconds: number): RunResult {
    const cut = this.stage.cutter.last;
    const s = this.stage.sando.stats();
    const kinds = this.stage.sando.layers.map((l) => l.kind.replace(/p+$/, ''));
    const matches = kinds.length === this.order.layers.length && kinds.every((k, i) => k === this.order.layers[i]);
    return {
      cut: cut ? { cv: cut.cv, quality: cut.quality, pieces: cut.pieces } : { cv: 1, quality: 'none', pieces: 0 },
      sando: { layers: s.layers, leanDeg: s.leanDeg, comDrift: s.comDrift, standing: s.standing, matches },
      service: { served: true, bowHeld, seconds, correct: matches && s.standing },
      floor: { splatMass: this.stage.cutter.stats().splatMass, drops: this.drops },
    };
  }

  /** Score the current sando as served. Sheathes on a clean correct serve,
   *  angers on the wrong dish. Returns the verdict for the scorecard. */
  serve(bowHeld = true, seconds = this.seconds): Verdict {
    const run = this.buildRun(bowHeld, seconds);
    const v = judge(this.order, run);
    if (run.service.correct && run.floor.splatMass < 1) this.samurai.bump('cleanServe');
    else if (!run.sando.matches || !run.sando.standing) this.samurai.bump('servedWrong');
    return v;
  }
}
