// Intro / landing animated moment
const { useState, useEffect, useRef } = React;

function Intro({ onDone }) {
  const [phase, setPhase] = useState(0);
  // phase 0: counter counting up, 1: name reveal, 2: fade out
  const [count, setCount] = useState(0);
  const startRef = useRef(null);

  useEffect(() => {
    startRef.current = performance.now();
    let raf;
    let cancelled = false;
    const DURATION = 1600;

    // Phase 0 lasts AT LEAST until fonts are loaded AND data.js has loaded.
    // The counter animates up to 99 and holds there until both the minimum
    // duration has elapsed AND both async loads have resolved — only then it
    // lands on 100 and we move on.
    //
    // document.fonts.ready alone isn't enough: it resolves once *requested*
    // fonts finish loading. The big "MAPA LÓPEZ" reveal uses families that
    // aren't actually painted until phase 1 (the loader is mono-only), so
    // they may not have been requested yet. We force-load every family the
    // name reveal might use — across all typography presets — so phase 1
    // never lands on a fallback font.
    const NAME_FAMILIES = [
      "Instrument Serif",
      "Newsreader",
      "Fraunces",
      "Archivo",
      "Inter",
      "Space Grotesk",
      "IBM Plex Sans",
      "JetBrains Mono",
      "Space Mono",
      "IBM Plex Mono",
    ];
    const preloadNameFonts = () => {
      if (!document.fonts || !document.fonts.load) return Promise.resolve();
      const text = "MAPA LÓPEZ";
      const jobs = [];
      for (const fam of NAME_FAMILIES) {
        // upright + italic, normal + medium — covers every variant the
        // various [data-display]/[data-typography] permutations select.
        for (const style of ["normal", "italic"]) {
          for (const weight of [400, 500]) {
            jobs.push(
              document.fonts.load(`${style} ${weight} 1em "${fam}"`, text)
                .catch(() => {})
            );
          }
        }
      }
      return Promise.all(jobs);
    };

    const fontsReady = Promise.all([
      (document.fonts && document.fonts.ready) ? document.fonts.ready : Promise.resolve(),
      preloadNameFonts(),
    ]);

    // Dynamically load data.js — if it's not already on window. We attach a
    // <script> tag and resolve on its load event (or immediately if it's
    // already been injected/loaded by a previous mount).
    const dataReady = new Promise((resolve, reject) => {
      if (window.MAPA_DATA) { resolve(); return; }
      const existing = document.querySelector('script[data-mapa-data]');
      if (existing) {
        if (window.MAPA_DATA) resolve();
        else {
          existing.addEventListener("load", () => resolve());
          existing.addEventListener("error", () => reject(new Error("data.js failed")));
        }
        return;
      }
      const s = document.createElement("script");
      s.src = "components/data.js?v=6";
      s.async = false;
      s.dataset.mapaData = "true";
      s.onload = () => resolve();
      s.onerror = () => reject(new Error("data.js failed"));
      document.head.appendChild(s);
    });

    let fontsDone = false;
    let dataDone = false;
    fontsReady.then(() => { fontsDone = true; });
    dataReady.then(() => {
      dataDone = true;
      // Once data is on window, kick off background preloading of every
      // image the user is about to see. This includes all three gallery
      // strips, motion thumbnails and shop images — the lightbox already
      // preloads neighbours on demand. We don't await this — the intro
      // shouldn't block on imagery, but starting the requests here means
      // most will already be cached by the time the user scrolls.
      try {
        const D = window.MAPA_DATA;
        const preload = window.Img && window.Img.preload;
        if (D && preload) {
          const collect = [];
          (D.LIVE_MUSIC || []).forEach((it) => {
            if (it.src) collect.push(it.src);
            if (it.lightbox && it.lightbox.images) {
              it.lightbox.images.forEach((c) => c.src && collect.push(c.src));
            }
          });
          (D.PORTRAITS || []).forEach((it) => it.src && collect.push(it.src));
          (D.DOCUMENTARY || []).forEach((it) => it.src && collect.push(it.src));
          (D.MOTION || []).forEach((v) => {
            if (v.id) collect.push(`https://img.youtube.com/vi/${v.id}/maxresdefault.jpg`);
          });
          (D.SHOP || []).forEach((p) => p.src && collect.push(p.src));
          // De-dupe and fire — Img.preload itself dedupes too.
          const seen = new Set();
          collect.forEach((src) => {
            if (seen.has(src)) return;
            seen.add(src);
            preload(src);
          });
        }
      } catch (err) {
        // Preloading is opportunistic — never block the intro on it.
        console.warn("preload skipped:", err);
      }
    }).catch((err) => {
      console.error(err);
      // Don't deadlock the intro if data fails — let it through anyway.
      dataDone = true;
    });

    const tick = (t) => {
      if (cancelled) return;
      const elapsed = t - startRef.current;
      const p = Math.min(elapsed / DURATION, 1);
      // ease-out
      const eased = 1 - Math.pow(1 - p, 3);

      if (p < 1 || !fontsDone || !dataDone) {
        // Cap at 99 while we're still waiting on fonts/data so the user
        // sees the loader is genuinely loading something.
        const capped = p < 1 ? Math.floor(eased * 99) : 99;
        setCount(capped);
        raf = requestAnimationFrame(tick);
      } else {
        setCount(100);
        setPhase(1);
        setTimeout(() => { if (!cancelled) setPhase(2); }, 900);
        setTimeout(() => { if (!cancelled) onDone && onDone(); }, 1500);
      }
    };
    raf = requestAnimationFrame(tick);
    return () => {
      cancelled = true;
      cancelAnimationFrame(raf);
    };
  }, []);

  const pad = (n) => String(n).padStart(3, "0");

  return (
    <div className={`intro ${phase >= 2 ? "intro--out" : ""}`}>
      <div className="intro__grid">
        <div className="intro__tl mono">
          <div>MAPA LÓPEZ</div>
          <div className="muted">MX · 2016 — 2026</div>
        </div>
        <div className="intro__tr mono muted">
          <div>CARGANDO MUESTRA</div>
          <div>{pad(count)} / 100</div>
        </div>
        <div className="intro__center">
          {phase >= 1 ? (
            <div className="intro__name">
              <span className="intro__name-line">MAPA</span>
              <span className="intro__name-line">LÓPEZ</span>
            </div>
          ) : (
            <div className="intro__bar-wrap mono">
              <div className="intro__bar">
                <div
                  className="intro__bar-fill"
                  style={{ width: `${count}%` }}
                />
              </div>
              <div className="intro__bar-meta">
                <span>FRAME {pad(count * 3)}</span>
                <span>ISO 1600</span>
                <span>f/1.4</span>
                <span>1/125s</span>
              </div>
            </div>
          )}
        </div>
        <div className="intro__bl mono muted">
          <div>FOTOGRAFÍA · DOCUMENTAL · PRODUCCIÓN AUDIOVISUAL</div>
        </div>
        <div className="intro__br mono muted">
          <div>N 19.4326°</div>
          <div>W 99.1332°</div>
        </div>
      </div>
    </div>
  );
}

window.Intro = Intro;
