/* ============================================================
   THE TIMELINE — interactive chronology of post-WWII energy
   events, presented as a full-screen PAGED experience: one
   event per page, all cards on a single side of a continuous
   spine, scroll-snapping like turning pages, with no visible
   scrollbar. Readers can filter by category and zoom to a
   period or year range. Each event is colour-coded by category.

   Reuses the global light-theme tokens from styles.css. Its own
   visual language lives under .tl-* in timeline.css. Data comes
   from timeline-data.js.
   ============================================================ */
const { useState: tlUse, useEffect: tlEffect, useMemo: tlMemo, useRef: tlRef, useCallback: tlCb } = React;

const TL_EVENTS = window.TIMELINE_EVENTS || [];
const TL_CATEGORIES = window.TIMELINE_CATEGORIES || [];
const TL_PERIODS = window.TIMELINE_PERIODS || [];

const TL_CAT_BY_CODE = TL_CATEGORIES.reduce((m, c) => { m[c.code] = c; return m; }, {});
const tlCatColor = (code) => (TL_CAT_BY_CODE[code] ? TL_CAT_BY_CODE[code].color : "#888");

const TL_YEAR_MIN = TL_EVENTS.reduce((m, e) => Math.min(m, e.year), 9999);
const TL_YEAR_MAX = TL_EVENTS.reduce((m, e) => Math.max(m, e.year), 0);

function tlPeriodLabel(code) {
  const p = TL_PERIODS.find((x) => x.code === code);
  return p ? code + " · " + p.label : code;
}

/* Autoplay speed presets (milliseconds each page is held). */
const TL_SPEEDS = [
  { label: "0.5×", ms: 3600 },
  { label: "1×",   ms: 2200 },
  { label: "2×",   ms: 1200 },
  { label: "4×",   ms: 650 },
];

/* ============================================================
   LOCATOR MAP
   A single shared borders-only world map (rendered once) that
   re-highlights the country/region of whichever event is centred.
   Geography comes from event.geo (Natural Earth country names,
   set in timeline-data.js); world-map-data.js supplies the paths.
   ============================================================ */
const TL_WORLD = (typeof window !== "undefined" && window.WORLD_MAP) || { width: 1000, height: 500, countries: [] };

/* Friendlier captions for region tokens and a few long country names. */
const TL_GEO_TOKEN_LABEL = {
  "@EU": "European Union",
  "@OPEC": "OPEC members",
  "@EEC6": "EEC founding six",
};
const TL_GEO_SHORT = {
  "United States of America": "United States",
  "United Arab Emirates": "UAE",
  "United Kingdom": "United Kingdom",
};
function tlGeoLabel(raw) {
  if (!raw || raw.length === 0) return "Global · multiple regions";
  return raw.map((t) => TL_GEO_TOKEN_LABEL[t] || TL_GEO_SHORT[t] || t).join(" · ");
}

/* Pre-build the static path list once; highlighting is a class toggle. */
function TlWorldShapes({ highlight }) {
  return TL_WORLD.countries.map((c) => (
    <path
      key={c.iso + c.name}
      className={"wm-country" + (highlight.has(c.name) ? " is-on" : "")}
      d={c.d}
    />
  ));
}
const TlWorldShapesMemo = React.memo(TlWorldShapes);

function TlLocator({ event, atIntro }) {
  const highlight = tlMemo(
    () => new Set(event && event.geo ? event.geo : []),
    [event]
  );
  const raw = event ? event.geoRaw : null;
  const label = atIntro ? "World overview" : tlGeoLabel(raw);
  const isGlobal = !atIntro && (!event || highlight.size === 0);
  const accent = event && event.cats && event.cats.length ? tlCatColor(event.cats[0]) : "var(--accent)";

  return (
    <div
      className={"tl-locator" + (atIntro ? " is-intro" : "") + (isGlobal ? " is-global" : "")}
      style={{ "--tl-accent": accent }}
      aria-hidden="true"
    >
      <svg
        className="tl-locator-map"
        viewBox={"0 0 " + TL_WORLD.width + " " + TL_WORLD.height}
        preserveAspectRatio="xMidYMid meet"
      >
        <g className="wm-land">
          <TlWorldShapesMemo highlight={highlight} />
        </g>
      </svg>
      <div className="tl-locator-cap">
        <span className="tl-locator-lbl">Location</span>
        <span className="tl-locator-place">{label}</span>
      </div>
    </div>
  );
}

/* ============================================================
   A single event page. The card sits on one side (right) of the
   continuous spine; a node anchors it to the rail.
   ============================================================ */
function TlPage({ ev, idx, active }) {
  const accent = tlCatColor(ev.cats[0]);
  const isActive = active;
  return (
    <section
      className={"tl-page " + (isActive ? "is-active " : "") + (ev.sidebar ? "is-major" : "")}
      style={{ "--tl-accent": accent }}
      data-idx={idx}
      aria-hidden={isActive ? "false" : "true"}
    >
      <div className="tl-page-inner">
        <div className="tl-page-stamp" aria-hidden="true">
          <span className="tl-page-year">{ev.year}</span>
          <span className="tl-page-era">{ev.period}</span>
        </div>

        <span className="tl-node" aria-hidden="true">
          {ev.cats.length > 1 ? (
            <span
              className="tl-node-split"
              style={{ background: "linear-gradient(135deg, " + tlCatColor(ev.cats[0]) + " 50%, " + tlCatColor(ev.cats[1]) + " 50%)" }}
            />
          ) : null}
        </span>
        <span className="tl-connector" aria-hidden="true" />

        <article className="tl-card">
          <header className="tl-card-head">
            <span className="tl-card-date">{ev.date}</span>
            <span className="tl-card-tags">
              {ev.cats.map((c) => (
                <span key={c} className="tl-tag" style={{ "--tl-tag": tlCatColor(c) }}>{c}</span>
              ))}
            </span>
          </header>

          <h3 className="tl-card-title">
            {ev.sidebar ? <span className="tl-card-flag" title="Major / sidebar event">◆</span> : null}
            <span className="tl-card-no">#{ev.n}</span>
            {ev.title}
          </h3>

          {ev.scale ? (
            <div className="tl-card-row tl-card-scale">
              <span className="tl-card-lbl">Scale</span><span>{ev.scale}</span>
            </div>
          ) : null}
          {ev.immediate ? (
            <div className="tl-card-row">
              <span className="tl-card-lbl">Immediate</span><span>{ev.immediate}</span>
            </div>
          ) : null}
          {ev.lasting ? (
            <div className="tl-card-row">
              <span className="tl-card-lbl">Lasting</span><span>{ev.lasting}</span>
            </div>
          ) : null}

          <footer className="tl-card-foot">
            <span className="tl-card-period">{tlPeriodLabel(ev.period)}</span>
            {ev.source ? <span className="tl-card-source">{ev.source}</span> : null}
          </footer>
        </article>
      </div>
    </section>
  );
}

/* ============================================================
   Collapsible controls: category filter, period quick-zoom,
   dual year-range zoom. Lives in a dropdown so each page can
   use nearly the full viewport height.
   ============================================================ */
function TlControlsPanel(props) {
  const {
    activeCats, toggleCat, allCats, noneCats,
    activePeriod, setPeriod,
    yearFrom, yearTo, setYearFrom, setYearTo, resetZoom,
  } = props;
  return (
    <div className="tl-panel">
      <div className="tl-panel-block">
        <div className="tl-controls-lbl">
          <span>Filter by category</span>
          <span className="tl-controls-actions">
            <button className="tl-mini" onClick={allCats}>All</button>
            <span className="tl-controls-sep">/</span>
            <button className="tl-mini" onClick={noneCats}>None</button>
          </span>
        </div>
        <div className="tl-chips">
          {TL_CATEGORIES.map((c) => {
            const on = activeCats.includes(c.code);
            return (
              <button key={c.code} className={"tl-chip " + (on ? "is-on" : "")}
                style={{ "--tl-chip": c.color }} onClick={() => toggleCat(c.code)} aria-pressed={on}>
                <span className="tl-chip-dot" />
                <span className="tl-chip-code">{c.code}</span>
                <span className="tl-chip-name">{c.label}</span>
              </button>
            );
          })}
        </div>
      </div>

      <div className="tl-panel-block">
        <div className="tl-controls-lbl"><span>Zoom to period</span></div>
        <div className="tl-periods">
          <button className={"tl-period " + (activePeriod === "ALL" ? "is-on" : "")} onClick={() => setPeriod("ALL")}>
            <b>All eras</b><small>{TL_YEAR_MIN}–{TL_YEAR_MAX}</small>
          </button>
          {TL_PERIODS.map((p) => (
            <button key={p.code} className={"tl-period " + (activePeriod === p.code ? "is-on" : "")} onClick={() => setPeriod(p.code)}>
              <b>{p.code} · {p.label}</b><small>{p.from}–{p.to}</small>
            </button>
          ))}
        </div>
      </div>

      <div className="tl-panel-block">
        <div className="tl-controls-lbl">
          <span>Zoom to years</span>
          <button className="tl-mini" onClick={resetZoom}>Reset</button>
        </div>
        <div className="tl-range">
          <div className="tl-range-readout">
            <span className="tl-range-val">{yearFrom}</span>
            <span className="tl-range-arrow">→</span>
            <span className="tl-range-val">{yearTo}</span>
          </div>
          <div className="tl-range-sliders">
            <input type="range" className="tl-slider tl-slider--from" min={TL_YEAR_MIN} max={TL_YEAR_MAX} step={1}
              value={yearFrom} onChange={(e) => setYearFrom(Math.min(Number(e.target.value), yearTo))} aria-label="Earliest year" />
            <input type="range" className="tl-slider tl-slider--to" min={TL_YEAR_MIN} max={TL_YEAR_MAX} step={1}
              value={yearTo} onChange={(e) => setYearTo(Math.max(Number(e.target.value), yearFrom))} aria-label="Latest year" />
          </div>
          <div className="tl-range-axis">
            {[1950, 1970, 1990, 2010, 2026].map((y) => <span key={y}>'{String(y).slice(2)}</span>)}
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   The view
   ============================================================ */
function TheTimelineView() {
  const allCatCodes = TL_CATEGORIES.map((c) => c.code);
  const [activeCats, setActiveCats] = tlUse(allCatCodes);
  const [activePeriod, setActivePeriod] = tlUse("ALL");
  const [yearFrom, setYearFrom] = tlUse(TL_YEAR_MIN);
  const [yearTo, setYearTo] = tlUse(TL_YEAR_MAX);
  const [panelOpen, setPanelOpen] = tlUse(false);
  const [active, setActive] = tlUse(0); // 0 = intro page; events are 1..N
  const [playing, setPlaying] = tlUse(false);
  const [speed, setSpeed] = tlUse(2200); // ms per page while playing

  const pagerRef = tlRef(null);
  const rafRef = tlRef(0);

  const toggleCat = (code) =>
    setActiveCats((prev) => (prev.includes(code) ? prev.filter((c) => c !== code) : [...prev, code]));
  const allCats = () => setActiveCats(allCatCodes);
  const noneCats = () => setActiveCats([]);

  const setPeriod = (code) => {
    setActivePeriod(code);
    if (code === "ALL") { setYearFrom(TL_YEAR_MIN); setYearTo(TL_YEAR_MAX); }
    else {
      const p = TL_PERIODS.find((x) => x.code === code);
      if (p) { setYearFrom(Math.max(p.from, TL_YEAR_MIN)); setYearTo(Math.min(p.to, TL_YEAR_MAX)); }
    }
  };
  const resetZoom = () => { setActivePeriod("ALL"); setYearFrom(TL_YEAR_MIN); setYearTo(TL_YEAR_MAX); };
  const setYearFromManual = (y) => { setActivePeriod("custom"); setYearFrom(y); };
  const setYearToManual = (y) => { setActivePeriod("custom"); setYearTo(y); };

  const filtered = tlMemo(() => TL_EVENTS.filter((ev) => {
    if (ev.year < yearFrom || ev.year > yearTo) return false;
    if (!ev.cats.some((c) => activeCats.includes(c))) return false;
    return true;
  }), [activeCats, yearFrom, yearTo]);

  const pageCount = filtered.length + 1; // + intro

  // Size the pager to fill the remaining viewport so the document
  // itself never scrolls — only the pager does (page by page).
  const sizePager = tlCb(() => {
    const el = pagerRef.current;
    if (!el) return;
    const top = el.getBoundingClientRect().top;
    const h = Math.max(280, window.innerHeight - top);
    el.style.height = h + "px";
  }, []);

  tlEffect(() => {
    sizePager();
    window.scrollTo({ top: 0, behavior: "auto" });
    const onResize = () => sizePager();
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [sizePager]);

  // Re-measure when the controls panel opens/closes (it changes the
  // toolbar height) and reset to the first page when filters change.
  tlEffect(() => { sizePager(); }, [panelOpen, sizePager]);
  tlEffect(() => {
    setActive(0);
    setPlaying(false);
    if (pagerRef.current) pagerRef.current.scrollTo({ top: 0, behavior: "auto" });
  }, [filtered.length, yearFrom, yearTo, activeCats]);

  // Track which page is centred so overlays + reveal stay in sync.
  const onScroll = tlCb(() => {
    if (rafRef.current) return;
    rafRef.current = requestAnimationFrame(() => {
      rafRef.current = 0;
      const el = pagerRef.current;
      if (!el) return;
      const idx = Math.round(el.scrollTop / el.clientHeight);
      setActive((prev) => (prev === idx ? prev : Math.max(0, Math.min(pageCount - 1, idx))));
    });
  }, [pageCount]);

  const goTo = tlCb((idx, behavior = "smooth") => {
    const el = pagerRef.current;
    if (!el) return;
    const clamped = Math.max(0, Math.min(pageCount - 1, idx));
    el.scrollTo({ top: clamped * el.clientHeight, behavior });
  }, [pageCount]);

  // Autoplay: while playing, advance one page every `speed` ms and
  // stop when the last page is reached. Re-scheduling off `active`
  // gives each page time to settle before the next turn.
  tlEffect(() => {
    if (!playing) return undefined;
    if (active >= pageCount - 1) { setPlaying(false); return undefined; }
    const id = setTimeout(() => goTo(active + 1), speed);
    return () => clearTimeout(id);
  }, [playing, active, pageCount, speed, goTo]);

  const togglePlay = tlCb(() => {
    setPlaying((p) => {
      if (!p && active >= pageCount - 1) goTo(0, "auto"); // restart from the top if at the end
      return !p;
    });
  }, [active, pageCount, goTo]);

  // Keyboard paging while the timeline is mounted.
  tlEffect(() => {
    const onKey = (e) => {
      if (panelOpen) return;
      const tag = (e.target && e.target.tagName) || "";
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      if (e.key === "ArrowDown" || e.key === "PageDown") { e.preventDefault(); setPlaying(false); goTo(active + 1); }
      else if (e.key === "ArrowUp" || e.key === "PageUp") { e.preventDefault(); setPlaying(false); goTo(active - 1); }
      else if (e.key === "Home") { e.preventDefault(); setPlaying(false); goTo(0, "auto"); }
      else if (e.key === "End") { e.preventDefault(); setPlaying(false); goTo(pageCount - 1, "auto"); }
      else if (e.key === " ") { e.preventDefault(); togglePlay(); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [active, pageCount, panelOpen, goTo, togglePlay]);

  const curEvent = active > 0 ? filtered[active - 1] : null;
  const activeCount = activeCats.length;
  const zoomLabel = activePeriod === "ALL" ? "All eras"
    : activePeriod === "custom" ? (yearFrom + "–" + yearTo)
    : tlPeriodLabel(activePeriod);

  return (
    <div className="tl tl--paged">
      {/* ---- compact toolbar ---- */}
      <div className="tl-top">
        <div className="tl-top-left">
          <span className="tl-bar-brand"><span className="tl-bar-dot" />The Timeline</span>
          <span className="tl-top-crumb">Post-WWII energy history · {TL_YEAR_MIN}–{TL_YEAR_MAX}</span>
        </div>
        <div className="tl-top-right">
          <span className="tl-legend-inline" aria-hidden="true">
            {TL_CATEGORIES.map((c) => (
              <span key={c.code} className={"tl-legend-mini " + (activeCats.includes(c.code) ? "is-on" : "")}
                title={c.label} style={{ "--tl-chip": c.color }}>
                <span className="tl-legend-dot" /> {c.code}
              </span>
            ))}
          </span>
          <button className={"tl-toggle " + (panelOpen ? "is-open" : "")} onClick={() => setPanelOpen((o) => !o)}
            aria-expanded={panelOpen}>
            <span className="tl-toggle-summary">
              <b>{activeCount === TL_CATEGORIES.length ? "All categories" : activeCount + " categories"}</b>
              <span className="tl-toggle-sep">·</span>
              <b>{zoomLabel}</b>
              <span className="tl-toggle-sep">·</span>
              <span className="tl-toggle-count">{filtered.length} events</span>
            </span>
            <span className="tl-toggle-caret">{panelOpen ? "▴" : "▾"}</span>
          </button>
        </div>

        {panelOpen ? (
          <TlControlsPanel
            activeCats={activeCats} toggleCat={toggleCat} allCats={allCats} noneCats={noneCats}
            activePeriod={activePeriod} setPeriod={setPeriod}
            yearFrom={yearFrom} yearTo={yearTo} setYearFrom={setYearFromManual} setYearTo={setYearToManual}
            resetZoom={resetZoom}
          />
        ) : null}
      </div>

      {/* ---- the paged stream ---- */}
      <div className="tl-pager" ref={pagerRef} onScroll={onScroll} tabIndex={-1}>
        <div className="tl-rail" aria-hidden="true" />

        {/* intro page */}
        <section className={"tl-page tl-page--intro " + (active === 0 ? "is-active" : "")}>
          <div className="tl-intro">
            <div className="tl-intro-eyebrow">A scrollable chronology · {filtered.length} of {TL_EVENTS.length} events</div>
            <h1 className="tl-intro-title">Eight decades of energy,<br /><em>one event at a time.</em></h1>
            <p className="tl-intro-lede">
              The major shocks, discoveries, disasters, technologies, policies and
              price ruptures that built the modern energy system — from the
              confirmation of Ghawar in 1948 to the Iberian blackout and the
              Twelve-Day War. Scroll, swipe or use the arrow keys to turn the
              pages. Filter by category or zoom into a single era from the bar
              above.
            </p>
            <button className="tl-intro-cta" onClick={() => goTo(1)}>
              <span>Begin</span><span className="tl-intro-cta-arrow">↓</span>
            </button>
          </div>
        </section>

        {filtered.length === 0 ? (
          <section className="tl-page tl-page--empty is-active">
            <div className="tl-empty">
              <div className="tl-empty-glyph">∅</div>
              <div>No events match the current filters.</div>
              <button className="tl-mini" onClick={() => { allCats(); resetZoom(); }}>Reset everything</button>
            </div>
          </section>
        ) : (
          filtered.map((ev, i) => (
            <TlPage key={ev.n} ev={ev} idx={i + 1} active={active === i + 1} />
          ))
        )}
      </div>

      {/* ---- overlays: era readout, paging, progress rail ---- */}
      {filtered.length > 0 ? (
        <React.Fragment>
          <TlLocator event={curEvent} atIntro={active === 0} />

          <div className={"tl-hud " + (active > 0 ? "is-on" : "")} aria-hidden="true">
            <span className="tl-hud-counter">
              <b>{String(active).padStart(2, "0")}</b>
              <span>/ {filtered.length}</span>
            </span>
            {curEvent ? (
              <span className="tl-hud-era">{curEvent.year} · {tlPeriodLabel(curEvent.period)}</span>
            ) : null}
          </div>

          <div className="tl-pagenav">
            <button className="tl-pagebtn" onClick={() => { setPlaying(false); goTo(active - 1); }} disabled={active <= 0} aria-label="Previous">▲</button>
            <button className="tl-pagebtn" onClick={() => { setPlaying(false); goTo(active + 1); }} disabled={active >= pageCount - 1} aria-label="Next">▼</button>
          </div>

          <div className={"tl-playbar " + (playing ? "is-playing" : "")}>
            <button className="tl-play" onClick={togglePlay}
              aria-label={playing ? "Pause autoplay" : "Play all events"} aria-pressed={playing}>
              {playing ? <span className="tl-play-pause"><i /><i /></span> : <span className="tl-play-tri" />}
              <span className="tl-play-lbl">{playing ? "Pause" : "Play all"}</span>
            </button>
            <span className="tl-speed" role="group" aria-label="Playback speed">
              <span className="tl-speed-lbl">Speed</span>
              {TL_SPEEDS.map((s) => (
                <button key={s.ms} className={"tl-speed-btn " + (speed === s.ms ? "is-on" : "")}
                  onClick={() => setSpeed(s.ms)} aria-pressed={speed === s.ms}>{s.label}</button>
              ))}
            </span>
          </div>

          <nav className="tl-progress" aria-label="Jump to event">
            {filtered.map((ev, i) => (
              <button
                key={ev.n}
                className={"tl-progress-tick " + (active === i + 1 ? "is-current" : "") + (active > i + 1 ? " is-past" : "")}
                style={{ "--tl-chip": tlCatColor(ev.cats[0]) }}
                title={ev.year + " · #" + ev.n + " " + ev.title.slice(0, 60)}
                onClick={() => goTo(i + 1)}
              />
            ))}
          </nav>

          {active === 0 && !playing ? <div className="tl-scrollcue" aria-hidden="true"><span>scroll</span><span className="tl-scrollcue-arr">↓</span></div> : null}
        </React.Fragment>
      ) : null}
    </div>
  );
}

window.TheTimelineView = TheTimelineView;
