/**
 * The samurai — toastsim's judge wearing armour. An anger meter that fills on
 * every kitchen sin and ebbs on a clean serve. Each quarter unsheaths the
 * katana one stage further; at full, he strikes: the string cut. He is the
 * fail-state AND the voice.
 */

export type AngerEvent =
  | 'drop' | 'splat' | 'crimeCut' | 'servedWrong' | 'servedLate' | 'offstage' | 'cleanServe';

/** How much each sin moves the meter (0..1). cleanServe sheathes. */
export const ANGER: Record<AngerEvent, number> = {
  drop: 0.06,
  splat: 0.14,
  crimeCut: 0.16,
  servedWrong: 0.34,
  servedLate: 0.03, // per ramp tick
  offstage: 0.3,
  cleanServe: -0.14,
};

/** 0 calm → 4 fury(strike). The 5 generated portraits map to these. */
export const PORTRAITS = ['samurai_calm', 'samurai_watch', 'samurai_annoyed', 'samurai_angry', 'samurai_fury'] as const;

export class Samurai {
  anger = 0; // 0..1
  ramp = 1; // day patience multiplier — sins hurt more on the rush-hour day
  struck = false;
  /** Fired once when the meter tops out — the stage wires the string cut here. */
  onStrike?: () => void;
  /** Fired on every meter change for the HUD/portrait. */
  onChange?: (anger: number, stage: number) => void;
  /** The last event, for barks. */
  last: AngerEvent | null = null;

  /** Apply an anger event (optionally overriding the amount, e.g. ramped late).
   *  Sins scale by the day ramp; sheathing (a clean serve) does not. */
  bump(ev: AngerEvent, amount = ANGER[ev]): void {
    this.last = ev;
    const scaled = amount > 0 ? amount * this.ramp : amount;
    this.anger = Math.max(0, Math.min(1, this.anger + scaled));
    this.onChange?.(this.anger, this.stage);
    if (this.anger >= 1 && !this.struck) this.strike();
  }

  /** 0..3 unsheath stage while alive; 4 once he has struck. */
  get stage(): number {
    if (this.struck) return 4;
    return this.anger < 0.25 ? 0 : this.anger < 0.5 ? 1 : this.anger < 0.75 ? 2 : 3;
  }

  get portrait(): string {
    return PORTRAITS[this.stage];
  }

  /** Vignette strength 0..1 for the screen darkening as he unsheaths. */
  get vignette(): number {
    return this.anger < 0.5 ? 0 : (this.anger - 0.5) / 0.5;
  }

  private strike(): void {
    this.struck = true;
    this.onChange?.(this.anger, 4);
    this.onStrike?.();
  }

  reset(): void {
    this.anger = 0;
    this.struck = false;
    this.last = null;
    this.onChange?.(0, 0);
  }
}
