// CV Website — main React app.
// Shared hooks + theme/lang state + toast.
// The Circuit variant component lives in circuit.jsx (loaded as a window global).

const { useState, useEffect, useCallback } = React;

/* ─── Hooks ──────────────────────────────────────────────────────────── */

// Scroll spy: returns id of section nearest the top.
function useScrollSpy(ids, offset = 120) {
  const [active, setActive] = useState(ids[0]);
  useEffect(() => {
    let raf = 0;
    const onScroll = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        const h = document.documentElement;
        // At (or near) the bottom the last section may never reach the offset
        // line — force it active so e.g. "Contact" highlights when clicked.
        if (h.scrollTop + h.clientHeight >= h.scrollHeight - 4) {
          setActive(ids[ids.length - 1]);
          return;
        }
        let best = ids[0];
        let bestDist = Infinity;
        for (const id of ids) {
          const el = document.getElementById(id);
          if (!el) continue;
          const r = el.getBoundingClientRect();
          // Distance of section top from offset line
          const d = Math.abs(r.top - offset);
          if (r.top - offset <= 0 && d < bestDist) {
            best = id;
            bestDist = d;
          }
        }
        setActive(best);
      });
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, [ids.join("|"), offset]);
  return active;
}

function smoothScrollTo(id) {
  const el = document.getElementById(id);
  if (!el) return;
  // Sections carry their own large top padding, so land the section top just
  // under the fixed topbar — the heading then sits comfortably high on screen.
  const top = el.getBoundingClientRect().top + window.scrollY - 16;
  window.scrollTo({ top, behavior: "smooth" });
}

// Persisted preference: localStorage-backed useState, falls back to default.
function usePref(key, def) {
  const [value, setValue] = useState(() => {
    try {
      const raw = localStorage.getItem("cv." + key);
      return raw == null ? def : JSON.parse(raw);
    } catch (e) {
      return def;
    }
  });
  const set = useCallback((v) => {
    setValue(v);
    try { localStorage.setItem("cv." + key, JSON.stringify(v)); } catch (e) {}
  }, [key]);
  return [value, set];
}

/* ─── Toast ──────────────────────────────────────────────────────────── */
function useToast() {
  const [msg, setMsg] = useState(null);
  const show = useCallback((m) => {
    setMsg(m);
    clearTimeout(window.__toastT);
    window.__toastT = setTimeout(() => setMsg(null), 2200);
  }, []);
  const node = (
    <div className={`toast ${msg ? "show" : ""}`}>{msg || "·"}</div>
  );
  return [node, show];
}

/* ─── App ────────────────────────────────────────────────────────────── */

const NAV_IDS = ["about", "experiences", "formation", "skills", "projects", "languages", "contact"];

function App() {
  const [lang, setLang] = usePref("lang", "fr");
  // Default to the visitor's OS theme; their manual toggle then persists.
  const [dark, setDark] = usePref(
    "dark",
    !window.matchMedia("(prefers-color-scheme: light)").matches
  );

  // Apply dark mode + document language on <html>
  useEffect(() => {
    document.documentElement.classList.toggle("dark", dark);
    document.documentElement.lang = lang;
  }, [dark, lang]);

  const [toastNode, showToast] = useToast();
  const active = useScrollSpy(NAV_IDS, 120);
  const [projectFilter, setProjectFilter] = useState("__all__");

  const handlers = {
    lang, dark, active, projectFilter,
    setLang, setDark, setProjectFilter,
    smoothScrollTo,
    showToast,
    copyEmail: () => {
      navigator.clipboard?.writeText(window.CV.identity.email).catch(() => {});
      showToast(window.tr(window.CV.ui.copied, lang));
    },
  };

  return (
    <React.Fragment>
      <div className="v-circuit">
        <CircuitVariant cv={window.CV} {...handlers} />
      </div>
      {toastNode}
    </React.Fragment>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
