/**
 * ゲーム状態の差分を見て効果音を鳴らす層。
 * シミュレーション側からは音を一切呼ばないので、sim/ は音の存在を知らないままでいられる。
 */
import { isMachine } from '../core/grid';
import { CatState, Phase, type HazardKind } from '../core/types';
import { isBreakTime, type GameState } from '../sim/state';
import { setAmbience } from './ambience';
import { setMusicIntensity } from './music';
import { playSfx, type SfxName } from './sfx';

interface Snapshot {
  shipped: number;
  defects: number;
  incidents: number;
  benchOutput: number;
  rank: number;
  paperDone: number;
  trucks: number;
  forkliftsMoving: number;
  onBreak: boolean;
  phase: Phase | null;
  catStates: Map<number, CatState>;
}

let prev: Snapshot | null = null;
let idleMeowAt = 0;

/** ヒヤリハット段階でどの鳴き声を当てるか */
const NEARMISS_SFX: Partial<Record<HazardKind, SfxName>> = {
  sleep_on_line: 'meow_soft',
  cross_line: 'meow_soft',
  no_helmet: 'meow_soft',
  boiler_nap: 'purr',
  scratch_machine: 'hiss',
  overflow: 'hiss',
  cable_bite: 'hiss',
  cat_punch: 'hiss',
  fur_contamination: 'meow_soft',
  blocked_route: 'meow',
  no_break: 'meow',
  paperwork: 'meow_soft',
  lathe_entangle: 'hiss',
  press_crush: 'hiss',
  robot_strike: 'panic',
  packer_trap: 'meow',
  forklift_run: 'panic',
  stack_collapse: 'thud',
  truck_load: 'meow',
};

const CAT_STATE_SFX: Partial<Record<CatState, SfxName>> = {
  [CatState.Panic]: 'panic',
  [CatState.Fighting]: 'punch',
  [CatState.Rage]: 'hiss',
  [CatState.Boxed]: 'purr',
  [CatState.Resting]: 'purr',
  [CatState.OnBreak]: 'purr',
  [CatState.Eating]: 'meow_soft',
  [CatState.Scratching]: 'meow_soft',
};

export function resetReactor(): void {
  prev = null;
}

function snapshot(s: GameState): Snapshot {
  let benchOutput = 0;
  for (const t of s.tiles) if (t.kind === 'bench') benchOutput += t.uses;
  return {
    shipped: s.shippedToday,
    defects: s.defectsToday,
    incidents: s.incidentsToday.length,
    benchOutput,
    rank: s.rank,
    paperDone: s.paperworkDoneToday,
    trucks: s.truckDeparturesToday,
    forkliftsMoving: s.forklifts.filter((f) => f.path.length > 0).length,
    onBreak: s.phase === Phase.Do && isBreakTime(s),
    phase: s.phase,
    catStates: new Map(s.cats.map((c) => [c.id, c.state])),
  };
}

export function reactToState(s: GameState): void {
  const cur = snapshot(s);

  // 環境音: 稼働中の機械が多いほど濃くする
  const machines = s.tiles.reduce((a, t) => a + (isMachine(t) && t.brokenFor <= 0 ? 1 : 0), 0);
  const running = s.phase === Phase.Do;
  setAmbience(running ? Math.min(0.95, 0.25 + machines / 70) : 0.07);
  // 稼働中はドラムとメロディまで入れ、計画中はコンピングとベースだけにする
  setMusicIntensity(running ? 1 : 0.15);

  if (!prev) {
    prev = cur;
    return;
  }

  if (cur.benchOutput > prev.benchOutput) playSfx('clank');
  if (cur.shipped > prev.shipped) playSfx('cash');
  if (cur.defects > prev.defects) playSfx('thud');

  // 新しく積まれたインシデントを鳴らし分ける
  for (let i = prev.incidents; i < cur.incidents; i++) {
    const inc = s.incidentsToday[i];
    if (!inc) continue;
    if (inc.severity === 'fatal' || inc.severity === 'serious') {
      playSfx('alarm');
      playSfx('panic');
    } else if (inc.severity === 'injury') {
      playSfx('siren');
      playSfx('panic');
    } else if (inc.severity === 'minor') {
      playSfx('siren');
    } else {
      playSfx(NEARMISS_SFX[inc.kind] ?? 'meow_soft');
    }
  }

  // 猫の状態遷移
  for (const cat of s.cats) {
    const before = prev.catStates.get(cat.id);
    if (before === cat.state) continue;
    const sfx = CAT_STATE_SFX[cat.state];
    if (sfx) playSfx(sfx, (cat.id % 5) - 2);
  }

  if (cur.paperDone > prev.paperDone) playSfx('paper');
  if (cur.trucks > prev.trucks) playSfx('horn');
  if (cur.forkliftsMoving > 0 && s.phase === Phase.Do) playSfx('beep');
  if (cur.onBreak && !prev.onBreak) playSfx('bell');
  if (cur.rank > prev.rank) playSfx('chime');

  // 稼働中はときどき誰かが鳴く
  if (s.phase === Phase.Do && s.cats.length > 0) {
    const now = performance.now();
    if (now - idleMeowAt > 5200 && Math.random() < 0.35) {
      idleMeowAt = now;
      playSfx('meow', Math.floor(Math.random() * 5) - 2);
    }
  }

  prev = cur;
}
