/* portfolio-app.jsx — root app, glue + admin wiring (v5)
   Adds: Product Ecosystem, Engineering Evolution, Dynamic Resume,
         Social Studio, Tweaks panel, design-token tweak side effects */

const { useState, useEffect } = React;

const ACCENT_TOKENS = {
  navy:    { accent: "#1e3a8a", accent2: "#2563eb", accent3: "#60a5fa" },
  gold:    { accent: "#b45309", accent2: "#d97706", accent3: "#fbbf24" },
  emerald: { accent: "#0d9488", accent2: "#14b8a6", accent3: "#5eead4" },
  violet:  { accent: "#6d28d9", accent2: "#7c3aed", accent3: "#a78bfa" }
};

const FONT_PAIRS = {
  "inter-instrument": {
    sans: "'Inter', ui-sans-serif, system-ui, sans-serif",
    serif: "'Instrument Serif', Georgia, serif",
    mono: "'JetBrains Mono', ui-monospace, monospace"
  },
  "geist-playfair": {
    sans: "'Geist Variable', 'Inter', ui-sans-serif, system-ui, sans-serif",
    serif: "'Playfair Display', Georgia, serif",
    mono: "'JetBrains Mono', ui-monospace, monospace"
  },
  "system-georgia": {
    sans: "system-ui, -apple-system, 'Helvetica Neue', sans-serif",
    serif: "Georgia, 'Times New Roman', serif",
    mono: "ui-monospace, 'SF Mono', Menlo, monospace"
  }
};

const App = () => {
  const [data, setData] = useState(() => loadStore());
  const [theme, setTheme] = useState(loadTheme() || data.settings?.theme || "light");
  const [adminOpen, setAdminOpen] = useState(false);
  const [adminTab, setAdminTab] = useState("start");
  const [openProj, setOpenProj] = useState(null);
  const [saveState, setSaveState] = useState({ ok: true });
  const { show } = React.useContext(ToastContext);

  // Persist on every change — and tell the truth about whether it worked.
  useEffect(() => {
    const res = saveStore(data);
    setSaveState(res);
    if (!res.ok) {
      show(res.reason === "quota"
        ? "⚠ Storage full — newest changes did NOT save. Remove a video/large image or use a link, then Export your data."
        : "⚠ Could not save changes to this browser.");
    }
  }, [data]);

  // Theme attribute + sync to data + persist (theme persists for everyone,
  // separate from content so it never pollutes the published state)
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    saveTheme(theme);
    setData(d => ({ ...d, settings: { ...d.settings, theme } }));
    // eslint-disable-next-line
  }, [theme]);

  // Hydrate from committed published content (public source of truth).
  // Runs once: visitors get published-over-defaults; admins keep their local
  // working draft layered on top (handled inside buildState).
  useEffect(() => {
    let cancelled = false;
    if (new URLSearchParams(location.search).get("admin") === "1") setAdminSession();
    (async () => {
      const published = await loadPublished();
      if (published && !cancelled) setData(buildState(published));
    })();
    return () => { cancelled = true; };
    // eslint-disable-next-line
  }, []);

  // Runtime meta-sync from config (canonical/OG). Helps in-browser; social
  // scrapers read static HTML, so the real domain must also be set there.
  useEffect(() => {
    const dep = data.config?.deployment;
    if (!dep || !dep.syncMeta || !dep.canonicalUrl) return;
    const base = String(dep.canonicalUrl).replace(/\/$/, "");
    const set = (sel, attr, val) => { const el = document.querySelector(sel); if (el && val) el.setAttribute(attr, val); };
    set('link[rel="canonical"]', "href", base + "/");
    set('meta[property="og:url"]', "content", base + "/");
    if (dep.ogImage) {
      const img = base + "/" + String(dep.ogImage).replace(/^\//, "");
      set('meta[property="og:image"]', "content", img);
      set('meta[name="twitter:image"]', "content", img);
    }
  }, [data.config?.deployment?.canonicalUrl, data.config?.deployment?.syncMeta, data.config?.deployment?.ogImage]);

  // Apply tweak-driven design tokens
  const settings = data.settings || {};
  useEffect(() => {
    const root = document.documentElement;
    // theme
    if (settings.theme && settings.theme !== theme) setTheme(settings.theme);
    // accent
    const tok = ACCENT_TOKENS[settings.accent] || ACCENT_TOKENS.navy;
    root.style.setProperty("--accent",   tok.accent);
    root.style.setProperty("--accent-2", tok.accent2);
    root.style.setProperty("--accent-3", tok.accent3);
    // font pair
    const fp = FONT_PAIRS[settings.fontPair] || FONT_PAIRS["inter-instrument"];
    root.style.setProperty("--sans",  fp.sans);
    root.style.setProperty("--serif", fp.serif);
    root.style.setProperty("--mono",  fp.mono);
    // density
    root.setAttribute("data-density",  settings.density || "comfortable");
    root.setAttribute("data-hero",     settings.heroVariant || "split");
    root.setAttribute("data-card",     settings.productCardStyle || "grid");
    // eslint-disable-next-line
  }, [settings.accent, settings.fontPair, settings.density, settings.heroVariant, settings.productCardStyle, settings.theme]);

  // Privacy-friendly analytics — only loads if a Plausible domain is configured
  // (off by default; no cookies, no PII). Skipped in admin sessions.
  useEffect(() => {
    const an = data.config?.analytics;
    if (!an || !an.plausibleDomain || isAdminSession()) return;
    if (document.getElementById("plausible-analytics")) return;
    const s = document.createElement("script");
    s.id = "plausible-analytics"; s.defer = true;
    s.setAttribute("data-domain", an.plausibleDomain);
    s.src = "https://plausible.io/js/script.js";
    document.head.appendChild(s);
  }, [data.config?.analytics?.plausibleDomain]);

  // Admin mode flag on <html>
  useEffect(() => {
    document.documentElement.classList.toggle("is-admin", adminOpen);
  }, [adminOpen]);

  // URL ?admin=1 auto-opens panel
  useEffect(() => {
    if (new URLSearchParams(location.search).get("admin") === "1") {
      setAdminOpen(true);
    }
  }, []);

  const update = (next) => {
    setData(next);
    show("Saved");
  };

  const toggleAdmin = () => setAdminOpen(o => { const next = !o; if (next) setAdminSession(); return next; });

  const onEdit = (section) => {
    setAdminTab(section);
    setAdminOpen(true);
    setAdminSession();
  };

  const onReset = () => {
    if (!confirm("Reset all content to defaults? This clears your edits.")) return;
    const fresh = resetStore();
    setData(fresh);
    show("Reset to defaults");
  };

  const vis = data.settings?.showSections || {};
  // Defensive: missing visibility key = visible (so new sections show on stale stores)
  const v = (key) => vis[key] !== false;
  const resumeVariant = data.settings?.resumeVariant || "product-eng";
  const setResumeVariant = (id) => {
    update({ ...data, settings: { ...data.settings, resumeVariant: id } });
  };

  return (
    <>
      <Topnav data={data} theme={theme} setTheme={setTheme} adminMode={adminOpen} onAdmin={toggleAdmin} />
      <Hero data={data} onEdit={onEdit} />
      {v("capabilities") && <HybridCapability       data={data} onEdit={onEdit} />}
      {v("process")      && <HowIBuild              data={data} onEdit={onEdit} />}
      {v("principles")   && <EngineeringPrinciples  data={data} onEdit={onEdit} />}
      {v("products")     && <ProductEcosystem       data={data} onEdit={onEdit} />}
      {v("frameworks")   && <Frameworks             data={data} onEdit={onEdit} />}
      {v("learning")     && <Learning               data={data} onEdit={onEdit} />}
      {v("showcase")     && <AppShowcase            data={data} onEdit={onEdit} />}
      {v("stack")        && <TechStack              data={data} onEdit={onEdit} />}
      {v("cases")        && <CaseStudies            data={data} />}
      {v("downloads")    && <Downloads              data={data} onEdit={onEdit} />}
      {v("social")       && <SocialStudio           data={data} onEdit={onEdit} />}
      {v("evolution")    && <EngineeringEvolution   data={data} onEdit={onEdit} />}
      {v("journey")      && <Journey                data={data} onEdit={onEdit} />}
      {v("resumeEngine") && <DynamicResume          data={data} onEdit={onEdit}
                                                   currentVariant={resumeVariant}
                                                   setVariant={setResumeVariant} />}
      {v("contact")      && <Contact                data={data} onEdit={onEdit} />}
      <Footer data={data} />

      {/* CMS gear is shown ONLY in an admin session (visit ?admin=1 once, or
          the lb_admin flag on your own browser). Public visitors never see it. */}
      {isAdminSession() && (
        <button
          className={"admin-launcher" + (adminOpen ? " active" : "")}
          onClick={toggleAdmin}
          title="Open CMS"
        >
          <Icon name={adminOpen ? "close" : "settings"} size={20} />
        </button>
      )}

      <AdminPanel
        open={adminOpen}
        onClose={() => setAdminOpen(false)}
        tab={adminTab}
        setTab={setAdminTab}
        data={data}
        update={update}
        onReset={onReset}
        saveState={saveState}
      />

      <TweaksPortfolio data={data} update={update} />

      {openProj && <ProjectModal project={openProj} onClose={() => setOpenProj(null)} />}
    </>
  );
};

const Root = () => (
  <ToastProvider>
    <App />
  </ToastProvider>
);

ReactDOM.createRoot(document.getElementById("root")).render(<Root />);
