// blog-app.jsx — Main app: composition, state, tweaks wiring

const { useState: useS, useEffect: useE, useMemo: useM } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "paletteKey": "gul-lala",
  "displayFont": "Cormorant Garamond",
  "bodyFont": "Karla",
  "fontSize": 16,
  "density": "regular",
  "showOrnaments": false,
  "motifIntensity": 0
}/*EDITMODE-END*/;

const DISPLAY_FONTS = ["Cormorant Garamond", "Playfair Display", "Cardo", "EB Garamond"];
const BODY_FONTS    = ["Karla", "Inter"];

function ColorSwatchPicker({ value, palettes, onChange }) {
  return (
    <div className="swatchset">
      {Object.entries(palettes).map(([key, p]) => (
        <button
          key={key}
          className={`swatch-row ${value === key ? "active" : ""}`}
          onClick={() => onChange(key)}
          title={p.name}
        >
          <span className="swatch-chips">
            <span className="chip" style={{ background: p.gold }}></span>
            <span className="chip" style={{ background: p.accent }}></span>
            <span className="chip" style={{ background: p.paper }}></span>
            <span className="chip" style={{ background: p.ink }}></span>
          </span>
          <span className="swatch-name">{p.name}</span>
          {value === key && <span className="swatch-check">✓</span>}
        </button>
      ))}
    </div>
  );
}

// ── Hash routing helpers
function parseHash() {
  const h = (window.location.hash || "").replace(/^#\/?/, "");
  if (h.startsWith("article/")) {
    return { route: "article", slug: h.slice("article/".length) };
  }
  if (h.startsWith("category/")) {
    return { route: "home", cat: h.slice("category/".length) };
  }
  if (h.startsWith("tag/")) {
    return { route: "home", tag: decodeURIComponent(h.slice("tag/".length)) };
  }
  if (h === "about") {
    return { route: "about" };
  }
  return { route: "home", cat: "home" };
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [query, setQuery] = useS("");
  const [submitOpen, setSubmitOpen] = useS(false);
  const [showAllArticles, setShowAllArticles] = useS(false);
  const [route, setRoute] = useS(() => parseHash());
  const [playing, setPlaying] = useS(null);

  // Hash → state. Reset search and scroll on every navigation. tagFilter
  // is derived from the hash itself, so it persists naturally.
  useE(() => {
    const onHash = () => {
      setRoute(parseHash());
      setQuery("");
      setShowAllArticles(false);
      window.scrollTo({ top: 0, behavior: "instant" });
    };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  const tagFilter = route.tag || null;
  const activeCat =
    route.route === "about" ? "about" :
    route.route === "home" && !route.tag ? (route.cat || "home") :
    "home";
  const articleOpen = route.route === "article"
    ? window.ARTICLES.find(a => a.id === route.slug)
    : null;
  const aboutOpen = route.route === "about";

  const openArticle = (a) => { window.location.hash = `#article/${a.id}`; };
  const goCategory  = (id) => {
    setQuery("");
    setShowAllArticles(false);
    const next =
      id === "home"  ? "" :
      id === "about" ? "#about" :
                       `#category/${id}`;
    if (window.location.hash === next || (next === "" && !window.location.hash)) {
      window.scrollTo({ top: 0, behavior: "smooth" });
    } else {
      window.location.hash = next;
    }
  };
  const goBack = () => { window.location.hash = activeCat && activeCat !== "home" ? `#category/${activeCat}` : ""; };
  const applyTag = (tag) => {
    window.location.hash = `#tag/${encodeURIComponent(tag)}`;
  };
  const clearTag = () => {
    window.location.hash = "";
  };

  const palette = window.PALETTES[t.paletteKey] || window.PALETTES["gold-maroon"];

  // Apply theme variables to :root
  useE(() => {
    const r = document.documentElement;
    r.style.setProperty("--gold", palette.gold);
    r.style.setProperty("--gold-deep", palette.goldDeep);
    r.style.setProperty("--accent", palette.accent);
    r.style.setProperty("--accent-soft", palette.accentSoft);
    r.style.setProperty("--ink", palette.ink);
    r.style.setProperty("--paper", palette.paper);
    r.style.setProperty("--paper-warm", palette.paperWarm);
    r.style.setProperty("--rule", palette.rule);
    r.style.setProperty("--font-display", `"${t.displayFont}", "Cormorant Garamond", Georgia, serif`);
    r.style.setProperty("--font-body", `"${t.bodyFont}", "Karla", system-ui, sans-serif`);
    r.style.setProperty("--font-size", `${t.fontSize}px`);
    r.style.setProperty("--density",
      t.density === "compact" ? "0.85" :
      t.density === "comfy" ? "1.15" : "1");
    r.style.setProperty("--ornaments", t.showOrnaments ? "1" : "0");
    r.style.setProperty("--motif-intensity", String((t.motifIntensity ?? 100) / 100));
  }, [palette, t.displayFont, t.bodyFont, t.fontSize, t.density, t.showOrnaments, t.motifIntensity]);

  const filtered = useM(() => {
    const q = query.trim().toLowerCase();
    return window.ARTICLES.filter((a) => {
      const matchCat = activeCat === "home" || a.category === activeCat;
      const matchTag = !tagFilter || (a.tags || []).includes(tagFilter);
      if (!q) return matchCat && matchTag;
      const bodyStr = Array.isArray(a.body)
        ? a.body.join(" ")
        : (a.body || "").replace(/<[^>]*>/g, " ");
      const haystack = [
        a.title, a.subtitle, a.excerpt, bodyStr,
        a.author || "",
        labelFor(a.category), a.category, a.pull || "",
        ...(a.tags || [])
      ].join(" ").toLowerCase();
      return matchCat && matchTag && haystack.includes(q);
    });
  }, [query, activeCat, tagFilter]);

  const featured = window.ARTICLES.find((a) => a.featured) || window.ARTICLES[0];
  const hideHero = !!query || !!tagFilter;
  const rest = (hideHero ? filtered : filtered.filter((a) => a.id !== featured.id));
  const isCategoryView = activeCat !== "home";
  const activeCatDef = window.CATEGORIES.find((c) => c.id === activeCat);

  const RECENT_LIMIT = 6;
  const canCollapse = !query && !tagFilter && !isCategoryView;
  const displayed = canCollapse && !showAllArticles ? rest.slice(0, RECENT_LIMIT) : rest;
  const hasMore = canCollapse && rest.length > RECENT_LIMIT && !showAllArticles;

  const goHome = () => goCategory("home");

  return (
    <div className={`site gl-site ${playing ? "is-playing" : ""}`} data-density={t.density}>
      <GLNav
        query={query}
        onSearch={(q) => { setQuery(q); if (articleOpen) goBack(); }}
        onClearSearch={() => setQuery("")}
        activeCat={activeCat}
        onOpenCategory={goCategory}
        onSubmit={() => setSubmitOpen(true)}
      />

      {aboutOpen ? (
        <GLAboutPage onGotoStories={() => goCategory("home")} />
      ) : articleOpen ? (
        <ArticlePage
          article={articleOpen}
          onBack={goBack}
          onOpen={openArticle}
          onTag={applyTag}
        />
      ) : activeCat === "podcast" ? (
        <GLPodcastPage />
      ) : (
        <GLLandingHome
          query={query}
          onSearch={(q) => { setQuery(q); }}
          activeCat={activeCat}
          tagFilter={tagFilter}
          onOpenCategory={goCategory}
          onOpen={openArticle}
          onSubmit={() => setSubmitOpen(true)}
          onGotoPodcast={() => goCategory("podcast")}
          featured={featured}
        />
      )}

      <GLFooter
        onSubmit={() => setSubmitOpen(true)}
        onAbout={() => goCategory("about")}
        onOpenCategory={goCategory}
        onGotoPodcast={() => goCategory("podcast")}
      />

      <SubmitStoryModal open={submitOpen} onClose={() => setSubmitOpen(false)} />

      {playing && (
        <div className="now-playing" role="region" aria-label="Now playing">
          <div className="np-inner">
            <div className="np-cover">
              <PodcastCover number={playing.number} size="small" tone="deep" image={playing.coverImage} />
            </div>
            <div className="np-text">
              <div className="eyebrow eyebrow-sm">Now playing · Ep. {String(playing.number).padStart(2, "0")}</div>
              <div className="np-title">{playing.title}</div>
              <div className="np-sub">{playing.guest} · {playing.durationStr}</div>
            </div>
            <div className="np-progress">
              <span className="np-time">00:42</span>
              <div className="np-bar"><span className="np-fill"></span></div>
              <span className="np-time">{playing.durationStr}</span>
            </div>
            <div className="np-controls">
              <button className="np-btn" aria-label="Previous">‹‹</button>
              <button className="np-btn np-btn-main" aria-label="Pause">▮▮</button>
              <button className="np-btn" aria-label="Next">››</button>
            </div>
            <button className="np-close" aria-label="Close player" onClick={() => setPlaying(null)}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7">
                <path d="M6 6l12 12M18 6l-12 12"/>
              </svg>
            </button>
          </div>
        </div>
      )}

      <TweaksPanel>
        <TweakSection label="Palette" />
        <ColorSwatchPicker
          value={t.paletteKey}
          palettes={window.PALETTES}
          onChange={(v) => setTweak("paletteKey", v)}
        />

        <TweakSection label="Typography" />
        <TweakSelect
          label="Display font"
          value={t.displayFont}
          options={DISPLAY_FONTS}
          onChange={(v) => setTweak("displayFont", v)}
        />
        <TweakSelect
          label="Body font"
          value={t.bodyFont}
          options={BODY_FONTS}
          onChange={(v) => setTweak("bodyFont", v)}
        />
        <TweakSlider
          label="Body size"
          value={t.fontSize}
          min={14} max={22} step={1} unit="px"
          onChange={(v) => setTweak("fontSize", v)}
        />
        <TweakRadio
          label="Density"
          value={t.density}
          options={["compact", "regular", "comfy"]}
          onChange={(v) => setTweak("density", v)}
        />

        <TweakSection label="Ornament" />
        <TweakToggle
          label="Gold rules & flourishes"
          value={t.showOrnaments}
          onChange={(v) => setTweak("showOrnaments", v)}
        />
        <TweakSlider
          label="Brand motif"
          value={t.motifIntensity}
          min={0} max={150} step={10} unit="%"
          onChange={(v) => setTweak("motifIntensity", v)}
        />
      </TweaksPanel>
    </div>
  );
}

function Root() {
  const [ready, setReady] = useS(false);
  useE(() => {
    if (typeof window.loadCMSData === "function") {
      window.loadCMSData()
        .then(() => setReady(true))
        .catch(() => setReady(true));
    } else {
      setReady(true);
    }
  }, []);
  if (!ready) {
    return (
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "center",
        height: "100vh", fontFamily: "Cormorant Garamond, serif",
        fontSize: "1.1rem", color: "#B68A55", letterSpacing: "0.05em",
      }}>
        Loading…
      </div>
    );
  }
  return <App />;
}

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