/**
 * LANE-UI — the research panel (T). The tech tree as media archaeology: you research
 * downward into older strata and upward into newer codecs (lore §6), so the eras are the
 * spine and each node is one ratified standard.
 *
 * Read-mostly: pick a node and it dispatches `setResearch`. Progress, active and unlocked
 * all come from `snapshot.research` — nothing here remembers anything.
 */
import type { GameData, ItemDef, SimSnapshot, TechDef, UIBus } from '../contracts';
import { createChipRow, type ChipRow } from './chips';
import { attrs, cls, el, panel, style, text } from './dom';
import { indexTech, techProgress, techStatus, type TechIndex } from './research';
import { COPY } from './voice';

export interface TechPanel {
  el: HTMLElement;
  toggle(): void;
  close(): void;
  isOpen(): boolean;
  update(snap: SimSnapshot): void;
}

interface NodeView {
  tech: TechDef;
  root: HTMLElement;
  fill: HTMLElement;
  chips: ChipRow;
  status: HTMLElement;
}

export function createTechPanel(data: GameData, bus: UIBus): TechPanel {
  const index: TechIndex = indexTech(data);
  const items = new Map<string, ItemDef>(data.items.map((i) => [i.id, i]));
  const machines = new Map(data.machines.map((m) => [m.id, m.name]));

  const root = panel();
  root.id = 'fk-tech';

  const closeBtn = el('button', 'fk-ins-x', [COPY.inspectorClose]);
  attrs(closeBtn, { type: 'button', title: 'CLOSE (T / ESC)' });
  const head = el('div', 'fk-ins-head', [el('div', 'fk-ins-name', [COPY.techHeader]), closeBtn]);
  const sub = el('div', 'fk-tech-sub', [COPY.techSubheader]);
  const offline = el('div', 'fk-tech-offline', [COPY.researchOffline]);
  const body = el('div', 'fk-tech-body');

  // ---- ABOUT / CREDITS footer (round 6). Cheap now, and gives the future synthetic-voice
  // credit a home before it exists (voice-cloning plan). Collapsed until asked for.
  const kv = (label: string, value: string) =>
    el('div', 'fk-credits-row', [el('span', 'fk-credits-k', [label]), el('span', 'fk-credits-v', [value])]);
  const credits = el('div', 'fk-credits', [
    el('div', 'fk-credits-title', [COPY.creditsTitle]),
    el('div', 'fk-credits-tag', [COPY.creditsTagline]),
    kv(COPY.creditsRepoLabel, COPY.creditsRepo),
    kv(COPY.creditsCodexLabel, COPY.creditsCodex),
    kv(COPY.creditsVoiceLabel, COPY.creditsVoice),
    el('div', 'fk-credits-slot', [COPY.creditsVoiceSlot]),
  ]);
  const aboutBtn = el('button', 'fk-credits-btn', [COPY.creditsButton]);
  attrs(aboutBtn, { type: 'button', title: 'ABOUT / CREDITS' });
  let creditsOpen = false;
  aboutBtn.addEventListener('click', () => {
    creditsOpen = !creditsOpen;
    cls(credits, 'is-on', creditsOpen);
    cls(aboutBtn, 'is-on', creditsOpen);
  });
  const footer = el('div', 'fk-tech-footer', [aboutBtn]);

  root.append(head, sub, offline, body, footer, credits);

  const views: NodeView[] = [];

  for (const { era, techs } of index.byEra) {
    body.append(el('div', 'fk-h fk-tech-era', [era.toUpperCase()]));
    const grid = el('div', 'fk-tech-grid');

    for (const tech of techs) {
      const name = el('div', 'fk-tech-name', [techLabel(tech)]);
      const status = el('div', 'fk-tech-status');
      const fill = el('div', 'fk-bar-f');
      const bar = el('div', 'fk-bar', [fill]);
      const chips = createChipRow(items);

      const node = el('button', 'fk-tech-node', [name, chips.el, bar, status]);
      attrs(node, { type: 'button', title: unlocksLabel(tech) });
      node.addEventListener('click', () => {
        // Clicking the active tech stands it down; clicking a done one does nothing.
        const done = views.find((v) => v.tech.id === tech.id)!.root.classList.contains('is-done');
        if (done) return;
        const active = root.querySelector('.fk-tech-node.is-active');
        bus.dispatch({ kind: 'setResearch', tech: active === node ? null : tech.id });
      });

      grid.append(node);
      views.push({ tech, root: node, fill, chips, status });
    }
    body.append(grid);
  }

  /** "SILVER SEAMS" — the era prefix is already the heading above it. */
  function techLabel(t: TechDef): string {
    return t.id.replace(new RegExp(`^${t.era}-`), '').replace(/-/g, ' ').toUpperCase();
  }

  function unlocksLabel(t: TechDef): string {
    const names = t.unlocks.map((u) => machines.get(u) ?? u.replace(/-/g, ' ').toUpperCase());
    return `UNLOCKS: ${names.join(', ')}`;
  }

  let open = false;
  function setOpen(v: boolean) {
    open = v;
    cls(root, 'is-open', v);
  }
  closeBtn.addEventListener('click', () => setOpen(false));

  return {
    el: root,
    toggle: () => setOpen(!open),
    close: () => setOpen(false),
    isOpen: () => open,
    update(snap) {
      if (!open) return; // 24 nodes is not worth a frame's work while shut
      const research = snap.research;

      // No research state at all means the sim isn't running research yet — say so
      // plainly rather than rendering a tree that silently can't do anything.
      cls(offline, 'is-on', !research);

      for (const v of views) {
        const st = techStatus(research, v.tech);
        cls(v.root, 'is-done', st === 'done');
        cls(v.root, 'is-active', st === 'active');

        style(v.fill.parentElement!, 'display', st === 'available' ? 'none' : '');
        if (st !== 'available') style(v.fill, 'width', `${techProgress(research, v.tech) * 100}%`);

        text(v.status, st === 'done' ? COPY.techDone : st === 'active' ? COPY.techActive : '');

        v.chips.update(
          Object.entries(v.tech.cost).map(([item, need]) => {
            const have = st === 'done' ? need : (research?.active === v.tech.id ? research.progress[item] ?? 0 : 0);
            return {
              item,
              count: `${Math.min(have, need)}/${need}`,
              state: have >= need ? ('met' as const) : ('plain' as const),
            };
          }),
        );
      }
    },
  };
}
