import { useId, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';
import type { Config } from 'vega-lite';
import { injectDatasets, type ProgressionStage } from '@core/lesson-parse';
import { formatSpec } from '@core/json-format';
import { changedLines } from '@core/spec-diff';
import { LandingChart } from '../landing/LandingChart';
import { Markdown } from './Markdown';
import styles from './SpecProgression.module.css';

/**
 * The deep-dive viewer: tabs across the stages of a spec's 80%→100% progression,
 * with the stage's source (changed lines highlighted vs. the previous stage) on
 * the left and its live chart on the right. The chart reuses the landing's
 * `LandingChart` — the one canonical chart-embed — so Vega stays lazy-loaded and
 * every render-lifecycle fix lives in one place.
 *
 * The source pane and diff show the *authored* spec (data referenced by name);
 * only the rendered chart gets the lesson's `datasets` injected, so a shared
 * dataset isn't repeated in every stage's visible source.
 *
 * Stages are a fixed, ordered authored list that never reorders, so the array
 * index is their identity (React key + tab/panel id wiring).
 *
 * Keyboard: an APG tablist with roving tabindex, Arrow/Home/End navigation, and
 * automatic activation (the panel is instant, so moving focus moves the stage).
 */
export function SpecProgression({
  stages,
  datasets,
  config,
}: {
  stages: ProgressionStage[];
  datasets: Record<string, unknown[]>;
  config: Config;
}): ReactNode {
  const [active, setActive] = useState(0);
  const tablistRef = useRef<HTMLDivElement>(null);
  const baseId = useId();

  const { lines, highlighted } = useMemo(() => {
    const text = formatSpec(stages[active].spec);
    const prev = active > 0 ? formatSpec(stages[active - 1].spec) : '';
    return {
      lines: text.split('\n'),
      highlighted: active > 0 ? changedLines(prev, text) : new Set<number>(),
    };
  }, [stages, active]);

  function focusTab(index: number): void {
    setActive(index);
    const tabs = tablistRef.current?.querySelectorAll<HTMLButtonElement>('[role="tab"]');
    tabs?.[index]?.focus();
  }

  function onKeyDown(e: KeyboardEvent<HTMLDivElement>): void {
    const last = stages.length - 1;
    let next: number;
    if (e.key === 'ArrowRight') next = active === last ? 0 : active + 1;
    else if (e.key === 'ArrowLeft') next = active === 0 ? last : active - 1;
    else if (e.key === 'Home') next = 0;
    else if (e.key === 'End') next = last;
    else return;
    e.preventDefault();
    focusTab(next);
  }

  const activeStage = stages[active];

  return (
    <div className={styles.progression}>
      <div
        className={styles.tabs}
        role="tablist"
        aria-label="Lesson stages"
        ref={tablistRef}
        onKeyDown={onKeyDown}
      >
        {stages.map((stage, i) => (
          <button
            key={i}
            id={`${baseId}-tab-${i}`}
            type="button"
            role="tab"
            aria-selected={i === active}
            aria-controls={`${baseId}-panel`}
            tabIndex={i === active ? 0 : -1}
            className={`${styles.tab} ${i === active ? styles.tabActive : ''}`}
            onClick={() => setActive(i)}
          >
            <span className={styles.tabNum}>{i + 1}</span>
            <span>{stage.label}</span>
          </button>
        ))}
      </div>

      <div
        id={`${baseId}-panel`}
        role="tabpanel"
        aria-labelledby={`${baseId}-tab-${active}`}
        className={styles.panel}
      >
        <div className={styles.panes}>
          <pre className={styles.source} aria-label="Spec source" tabIndex={0}>
            <code>
              {lines.map((line, i) => (
                <span
                  // Lines have no stable identity; index is the intended key here.
                  key={i}
                  className={`${styles.line} ${highlighted.has(i) ? styles.added : ''}`}
                >
                  {line || ' '}
                </span>
              ))}
            </code>
          </pre>
          <div className={styles.chartPane}>
            <LandingChart
              spec={injectDatasets(activeStage.spec, datasets)}
              config={config}
              className={styles.chart}
            />
          </div>
        </div>
        <Markdown className={styles.note} source={activeStage.note} />
      </div>
    </div>
  );
}
