// Bruno — personal site. Standalone polished build of the editorial-minimal
// direction. Single-column, Lora throughout, one blue accent.
//
// Architecture:
//   - useHashRoute()  — '#/' is home, '#/a/<id>' is a writing entry
//   - useTweaks()     — comes from tweaks-panel.jsx (state + persist)
//   - applyTheme()    — writes CSS variables onto <html> so every node
//                       (incl. Cmd-K and Tweaks) responds to one source
//                       of theme truth instead of prop-drilling colors
//   - <CmdK/>         — theme-aware command palette, ⌘K or /
//   - <TweaksPanel/>  — wired by the host toolbar toggle

const { useEffect, useState, useMemo, useRef, useCallback } = React;

// ---------- routing ----------
function parseHash() {
  const h = (window.location.hash || "").replace(/^#\/?/, "");
  if (h.startsWith("a/")) return { kind: "article", id: h.slice(2) };
  return { kind: "home" };
}
function useHashRoute() {
  const [route, setRoute] = useState(parseHash);
  useEffect(() => {
    const on = () => {
      setRoute(parseHash());
      window.scrollTo({ top: 0, behavior: "instant" });
    };
    window.addEventListener("hashchange", on);
    return () => window.removeEventListener("hashchange", on);
  }, []);
  return route;
}

// ---------- accent palette ----------
// Keyed by the light-mode hex so it works directly with <TweakColor>.
// Each accent has a paired dark-mode hex (a lighter, slightly desaturated
// version) so contrast holds when the page flips.
const ACCENT_OPTIONS = ["#2563eb", "#c8553d", "#2f6a4a", "#3f3f46"];
const ACCENT_DARK = {
  "#2563eb": "#7aa7ff",
  "#c8553d": "#e08068",
  "#2f6a4a": "#7fbb9c",
  "#3f3f46": "#a1a1aa",
};

// ---------- theme application ----------
function applyTheme(t) {
  const r = document.documentElement.style;
  const accentLight = t.accent || ACCENT_OPTIONS[0];
  const accentDark = ACCENT_DARK[accentLight] || accentLight;
  if (t.dark) {
    r.setProperty("--bg",   "#0d0d0c");
    r.setProperty("--card", "#141413");
    r.setProperty("--ink",  "#ece9e3");
    r.setProperty("--mute", "#8a8780");
    r.setProperty("--soft", "#a5a29b");
    r.setProperty("--hair", "#2a2826");
    r.setProperty("--accent", accentDark);
  } else {
    r.setProperty("--bg",   "#ffffff");
    r.setProperty("--card", "#fafaf8");
    r.setProperty("--ink",  "#0a0a0a");
    r.setProperty("--mute", "#737373");
    r.setProperty("--soft", "#525252");
    r.setProperty("--hair", "#e8e8e6");
    r.setProperty("--accent", accentLight);
  }
  // density: --gap multiplier for vertical rhythm
  r.setProperty("--gap", t.density === "airy" ? "1.25" : t.density === "cozy" ? "0.85" : "1");
  r.setProperty("--bodysize", String(t.bodysize || 17) + "px");
  document.body.style.background = "var(--bg)";
  document.body.style.color = "var(--ink)";
}

// ---------- shared bits ----------
function ELink({ href = "#", children, italic = false, mute = false, style = {} }) {
  return (
    <a href={href} className={"el" + (mute ? " el-mute" : "")} style={{
      fontStyle: italic ? "italic" : "inherit",
      ...style,
    }}>{children}</a>
  );
}

const fmtShort = (iso) => new Date(iso + "T12:00:00").toLocaleDateString("en-US", { year: "2-digit", month: "short", day: "2-digit" });
const fmtLong  = (iso) => new Date(iso + "T12:00:00").toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" });

// ---------- home ----------
function Home({ t }) {
  return (
    <div className="page">
      <div className="col">
        <header className="site-head">
          <div className="name-row">
            <span className="name">{SITE.fullName}</span>
            <span className="tag">{SITE.tagline}</span>
          </div>
          <div className="head-actions">
            <a
              className="gh-icon"
              href="https://github.com/bruno353"
              target="_blank"
              rel="noopener noreferrer"
              aria-label="GitHub"
            >
              <svg viewBox="0 0 16 16" width="18" height="18" fill="currentColor" aria-hidden="true">
                <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
              </svg>
            </a>
            <button className="kbd" onClick={() => window.dispatchEvent(new CustomEvent("cmdk:open"))}>
              <span>⌘K</span>
            </button>
          </div>
        </header>

        <section className="bio">
          <p className="lede">{SITE.bioShort}</p>
          <p className="lede-sub">{SITE.bioLong}</p>
        </section>

        {ENTRIES.length > 0 && (
          <Group title="Writing">
            <ul className="rows">
              {ENTRIES.map(e => (
                <li key={e.id} className="row">
                  <div className="row-main">
                    <ELink href={`#/a/${e.id}`}>{e.title}</ELink>
                    {t.showKinds && <KindLabel kind={e.kind} />}
                    {t.showReading && <span className="row-min">{e.reading}</span>}
                  </div>
                  <span className="row-date">{fmtShort(e.date)}</span>
                </li>
              ))}
            </ul>
          </Group>
        )}

        {PROJECTS.length > 0 && (
          <Group title="Projects">
            <ul className="rows">
              {PROJECTS.map(p => (
                <li key={p.id} className="row-stacked">
                  <div className="row-stacked-head">
                    <ELink href="#" italic>{p.name}</ELink>
                    <span className="meta-mono">{p.meta}</span>
                  </div>
                  <p className="blurb">{p.blurb}</p>
                </li>
              ))}
            </ul>
          </Group>
        )}

        {FINDS.length > 0 && (
          <Group title="Finds">
            <ul className="rows">
              {FINDS.map(f => (
                <li key={f.id} className="find">
                  <div className="find-head">
                    <span className="find-date">{fmtShort(f.date)}</span>
                    <ELink href={f.url}>{f.title}</ELink>
                    <span className="find-host">{f.host}</span>
                  </div>
                  <p className="find-note">{f.note}</p>
                </li>
              ))}
            </ul>
          </Group>
        )}

        <footer className="site-foot">
          <span className="socials">
            {SITE.social.map(s => <ELink key={s.label} href={s.url} mute>{s.label}</ELink>)}
          </span>
        </footer>
      </div>
    </div>
  );
}

function Group({ title, children }) {
  return (
    <section className="group">
      <h2 className="group-title">{title}</h2>
      {children}
    </section>
  );
}

function KindLabel({ kind }) {
  const label = { research: "research", essay: "essay", note: "note" }[kind] || kind;
  return <span className="kind-label">{label}</span>;
}

// ---------- inline markup in article blocks ----------
// Supports `inline code` and **bold**. Applied to p, h2, and quote text.
function renderInline(text) {
  const parts = text.split(/(`[^`]+`|\*\*[^*]+\*\*)/g);
  return parts.map((part, i) => {
    if (!part) return null;
    if (part.startsWith("`") && part.endsWith("`") && part.length >= 2) {
      return <code key={i} className="inline-code">{part.slice(1, -1)}</code>;
    }
    if (part.startsWith("**") && part.endsWith("**") && part.length >= 4) {
      return <strong key={i}>{part.slice(2, -2)}</strong>;
    }
    return <React.Fragment key={i}>{part}</React.Fragment>;
  });
}

// ---------- article reader ----------
function Reader({ id }) {
  const entry = ENTRIES.find(e => e.id === id);
  if (!entry) {
    return (
      <div className="page">
        <div className="col">
          <ELink href="#/" mute>← Back</ELink>
          <h1 className="missing">No piece here.</h1>
          <p className="lede-sub">That id ({id}) doesn't match anything. The link might be old, or I might have renamed something.</p>
        </div>
      </div>
    );
  }

  const body = getArticleBody(id);

  return (
    <div className="page">
      <div className="col col-narrow">
        <div className="reader-nav">
          <ELink href="#/" mute>← Back to all writing</ELink>
        </div>

        <article className="article">
          <div className="art-kind">
            <KindLabel kind={entry.kind} />
            <span className="meta-mono">{fmtLong(entry.date)} · {entry.reading}</span>
          </div>
          <h1 className="art-title">{entry.title}</h1>
          <p className="art-summary">{entry.summary}</p>

          <div className="art-body">
            {body.map((block, i) => {
              if (block.kind === "h2") return <h2 key={i}>{renderInline(block.text)}</h2>;
              if (block.kind === "quote") return <blockquote key={i}>{renderInline(block.text)}</blockquote>;
              if (block.kind === "code") return <pre key={i} className="art-code"><code>{block.text}</code></pre>;
              return <p key={i}>{renderInline(block.text)}</p>;
            })}
          </div>

          <div className="art-tags">
            {entry.tags.map(tag => <span key={tag} className="tag-chip">#{tag}</span>)}
          </div>

          <div className="art-foot">
            <ELink href="#/" mute>← All writing</ELink>
            <span className="meta-mono">{fmtLong(entry.date)}</span>
          </div>
        </article>
      </div>
    </div>
  );
}

// ---------- Cmd-K palette (theme-aware) ----------
function CmdKPalette() {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const inputRef = useRef(null);

  useEffect(() => {
    const onKey = (e) => {
      const tag = (e.target.tagName || "").toLowerCase();
      const inField = tag === "input" || tag === "textarea";
      const isK = (e.key === "k" || e.key === "K") && (e.metaKey || e.ctrlKey);
      const isSlash = e.key === "/" && !inField;
      if (isK || isSlash) { e.preventDefault(); setOpen(v => !v); }
      else if (e.key === "Escape") setOpen(false);
    };
    const onCustom = () => setOpen(true);
    window.addEventListener("keydown", onKey);
    window.addEventListener("cmdk:open", onCustom);
    return () => {
      window.removeEventListener("keydown", onKey);
      window.removeEventListener("cmdk:open", onCustom);
    };
  }, []);

  useEffect(() => {
    if (open && inputRef.current) {
      setTimeout(() => inputRef.current.focus(), 30);
      setQ("");
    }
  }, [open]);

  const results = useMemo(() => {
    const idx = window.SEARCH_INDEX || [];
    if (!q.trim()) return idx.slice(0, 8);
    const n = q.toLowerCase();
    return idx.filter(r => r.title.toLowerCase().includes(n) || (r.sub || "").toLowerCase().includes(n)).slice(0, 12);
  }, [q]);

  const grouped = useMemo(() => {
    const g = {};
    results.forEach(r => { (g[r.group] ||= []).push(r); });
    return g;
  }, [results]);

  if (!open) return null;

  const go = (r) => {
    setOpen(false);
    if (r.group === "Writing") {
      const e = ENTRIES.find(x => x.title === r.title);
      if (e) window.location.hash = `#/a/${e.id}`;
    }
  };

  return (
    <div className="cmdk-scrim" onClick={() => setOpen(false)}>
      <div className="cmdk" onClick={(e) => e.stopPropagation()}>
        <div className="cmdk-head">
          <span className="cmdk-prefix">⌘K</span>
          <input
            ref={inputRef}
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Search writing, projects, finds…"
          />
        </div>
        <div className="cmdk-body">
          {Object.keys(grouped).length === 0 && (
            <div className="cmdk-empty">Nothing here. Try a different word.</div>
          )}
          {Object.entries(grouped).map(([group, items]) => (
            <div key={group}>
              <div className="cmdk-group">{group}</div>
              {items.map((r, i) => (
                <div key={r.title + i} className="cmdk-row" onClick={() => go(r)}>
                  <div className="cmdk-title">{r.title}</div>
                  <div className="cmdk-sub">{r.sub}</div>
                </div>
              ))}
            </div>
          ))}
        </div>
        <div className="cmdk-foot">
          <span>↵ open · esc close · / or ⌘K to toggle</span>
          <span>{results.length} results</span>
        </div>
      </div>
    </div>
  );
}

// ---------- Tweaks (uses helpers from tweaks-panel.jsx) ----------
function Tweaks({ t, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Appearance">
        <TweakColor
          label="Accent"
          value={t.accent}
          onChange={(v) => setTweak("accent", v)}
          options={ACCENT_OPTIONS}
        />
        <TweakToggle label="Dark mode" value={t.dark} onChange={(v) => setTweak("dark", v)} />
        <TweakRadio
          label="Density"
          value={t.density}
          onChange={(v) => setTweak("density", v)}
          options={["cozy", "normal", "airy"]}
        />
        <TweakSlider
          label="Body size"
          value={t.bodysize}
          onChange={(v) => setTweak("bodysize", v)}
          min={14} max={20} step={1} unit="px"
        />
      </TweakSection>

      <TweakSection label="Content">
        <TweakToggle label="Show kind labels" value={t.showKinds} onChange={(v) => setTweak("showKinds", v)} />
        <TweakToggle label="Show reading time" value={t.showReading} onChange={(v) => setTweak("showReading", v)} />
        <TweakToggle label="Show 'now' line" value={t.showNow} onChange={(v) => setTweak("showNow", v)} />
        <TweakText label="Now text" value={t.nowText} onChange={(v) => setTweak("nowText", v)} placeholder="What you're up to" />
      </TweakSection>

      <TweakSection label="Identity">
        <TweakText label="Name" value={t.name} onChange={(v) => setTweak("name", v)} />
        <TweakText label="Tagline" value={t.tagline} onChange={(v) => setTweak("tagline", v)} />
      </TweakSection>
    </TweaksPanel>
  );
}

// ---------- App ----------
function App() {
  const [t, setTweak] = useTweaks(window.TWEAK_DEFAULTS);
  const route = useHashRoute();

  // patch SITE display values from tweaks (kept as a live override so the
  // user can edit name/tagline without forking data.jsx).
  useEffect(() => {
    SITE.fullName = t.name || SITE.fullName;
    SITE.tagline  = t.tagline || SITE.tagline;
  }, [t.name, t.tagline]);

  useEffect(() => { applyTheme(t); }, [t]);

  return (
    <>
      {route.kind === "home" ? <Home t={t} /> : <Reader id={route.id} />}
      <CmdKPalette />
      <Tweaks t={t} setTweak={setTweak} />
    </>
  );
}

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