/**
 * LANE-UI — the session tally of The Correction's edits (round 6).
 *
 * A `repaired` event (contracts v8) is a parity mite quietly "fixing" one of your
 * artifacts back into clean product at a tile. Nothing in the snapshot remembers this —
 * the event is edge-triggered and gone — so the UI keeps its own running count for the
 * two places that narrate the raid: the top-strip ticker (per-item totals) and the
 * inspector's FIREWALL LOG (per-unit catches, resolved by footprint).
 *
 * This is session chrome, not sim truth: it resets when the page reloads, which is the
 * honest thing to do since the sim doesn't persist it either.
 */
import type { SimEvent, Vec2 } from '../contracts';

interface Repair {
  x: number;
  y: number;
  item: string;
}

export interface Corrections {
  /** Record one `repaired` event. Ignores anything else, so callers can pass it raw. */
  record(ev: SimEvent): void;
  /** How many edits total this session. 0 before the first mite ever appears. */
  total(): number;
  /** Per-item totals, newest-touched first, for the ticker chip row. */
  byItem(): { item: string; count: number }[];
  /** Edits that landed on the given footprint rect — the inspector's per-unit "catches". */
  catchesOn(pos: Vec2, footprint: Vec2): number;
}

export function createCorrections(): Corrections {
  const repairs: Repair[] = [];
  // Insertion-ordered per-item counts (Map preserves first-seen order).
  const perItem = new Map<string, number>();

  return {
    record(ev) {
      if (ev.kind !== 'repaired') return;
      repairs.push({ x: ev.pos.x, y: ev.pos.y, item: ev.item });
      perItem.set(ev.item, (perItem.get(ev.item) ?? 0) + 1);
    },
    total: () => repairs.length,
    byItem: () => [...perItem].map(([item, count]) => ({ item, count })),
    catchesOn(pos, footprint) {
      let n = 0;
      for (const r of repairs) {
        if (
          r.x >= pos.x && r.x < pos.x + footprint.x &&
          r.y >= pos.y && r.y < pos.y + footprint.y
        ) n++;
      }
      return n;
    },
  };
}
