/**
 * LANE-UI — event toasts, bottom right. Max 4 on screen, 6s each, oldest evicted.
 *
 * Wall-clock timing is fine here: toasts are chrome, not sim. The determinism rule in
 * MASTERPLAN binds `src/sim/**`, and nothing in this file feeds back into it.
 */
import type { SimEvent } from '../contracts';
import { cls, el } from './dom';
import { eventToast } from './voice';

export interface Toasts {
  el: HTMLElement;
  /** `itemName` resolves item ids for events that name an item (e.g. v8 `repaired`). */
  push(
    ev: SimEvent,
    machineName: (entityId: number) => string,
    itemName?: (id: string) => string,
  ): void;
  /** A toast that isn't a raw sim event — onboarding hints, deduped warnings, ceremony. */
  pushMessage(msg: string, kind: string): void;
  /** Stand the stack off the bottom-right corner once the tuner dock moves in there. */
  setStandoff(on: boolean): void;
  /** Called once per frame to expire old lines. */
  update(nowMs: number): void;
}

const TTL_MS = 6000;
const FADE_MS = 400; // must match the .fk-toast.is-out transition
const MAX = 4;

export function createToasts(): Toasts {
  const root = el('div');
  root.id = 'fk-toasts';

  const live: Array<{ node: HTMLElement; dieAt: number }> = [];

  function retire(entry: { node: HTMLElement; dieAt: number }) {
    cls(entry.node, 'is-out', true);
    setTimeout(() => entry.node.remove(), FADE_MS);
  }

  function emit(msg: string, kind: string) {
    const node = el('div', `fk-toast k-${kind}`, [msg]);
    root.append(node);
    live.push({ node, dieAt: performance.now() + TTL_MS });
    while (live.length > MAX) retire(live.shift()!);
  }

  return {
    el: root,
    push(ev, machineName, itemName) {
      const msg = eventToast(ev, machineName, itemName);
      if (msg) emit(msg, ev.kind);
    },
    pushMessage: emit,
    setStandoff(on) {
      cls(root, 'is-standoff', on);
    },
    update(nowMs) {
      while (live.length && live[0].dieAt <= nowMs) retire(live.shift()!);
    },
  };
}
