/* global React, ReactDOM, ALL_RECIPES */
const { useState, useMemo } = React;

const tierFor = (score) => {
  if (score >= 9) return { label: "High", className: "tier-high", hex: "#3f7d3a" };
  if (score >= 7) return { label: "Medium", className: "tier-med", hex: "#a06b13" };
  return { label: "Low", className: "tier-low", hex: "#a8392b" };
};

// Warm palette pairs for card thumbs — hashed per-slug for stable variety
const PALETTES = [
  ["#c87f3e", "#5e3a1a"], ["#a06b35", "#3d2412"], ["#b9341e", "#3d0e08"],
  ["#5b2a55", "#1f0f23"], ["#7a3a1f", "#2a1409"], ["#d96a3a", "#582518"],
  ["#3a5022", "#0e1a08"], ["#a76536", "#3a1c0a"], ["#d49a32", "#5a3a0a"],
  ["#8c2a18", "#2c0a04"], ["#c44a1e", "#3e1208"], ["#d4a256", "#5a3614"],
  ["#3a1c0b", "#0d0604"], ["#6a3320", "#1f0a06"],
];
function hashSlug(s) {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return h;
}
function parseMinutes(s) {
  if (!s || typeof s !== "string") return 0;
  const str = s.toLowerCase();
  let mins = 0;
  const hr = str.match(/(\d+(?:\.\d+)?)\s*(?:hr|hour)/);
  if (hr) mins += parseFloat(hr[1]) * 60;
  const mn = str.match(/(\d+)\s*min/);
  if (mn) mins += parseInt(mn[1], 10);
  if (!mins) {
    const justNum = str.match(/^(\d+)$/);
    if (justNum) mins = parseInt(justNum[1], 10);
  }
  return mins;
}
function blurbFor(r) {
  // First instruction trimmed to ~110 chars
  const first = r.instructions && r.instructions[0] && r.instructions[0].text;
  if (first) return first.length > 110 ? first.slice(0, 107).trimEnd() + "…" : first;
  return r.title;
}
function titleCase(s) {
  return s.replace(/(^|[\s\-_])(\w)/g, (_, p, c) => p.replace("_", " ").replace("-", " ") + c.toUpperCase());
}

const DATA = Object.entries(window.ALL_RECIPES).map(([slug, r]) => {
  const m = r.metadata || {};
  const prep = parseMinutes(m.prep_time);
  const cook = parseMinutes(m.cook_time);
  return {
    slug,
    title: r.title || titleCase(slug),
    blurb: blurbFor(r),
    source_type: r.source_type || "handwritten",
    cook_time: m.cook_time || "—",
    total_min: prep + cook || hashSlug(slug) % 90,
    servings: m.servings ?? "—",
    difficulty: m.difficulty || "—",
    tags: Array.isArray(m.tags) ? m.tags : [],
    confidence: r.confidence_assessment?.overall_score ?? 0,
    ambiguities: r.confidence_assessment?.ambiguities?.length ?? 0,
    iteration: r.confidence_assessment?.iteration ?? 1,
    gated: r.confidence_assessment?.gated ?? false,
    color: PALETTES[hashSlug(slug) % PALETTES.length],
    href: `Recipe.html?slug=${encodeURIComponent(slug)}`,
  };
});

// ───────────────────────────── App ─────────────────────────────

function App() {
  const [q, setQ] = useState("");
  const [tier, setTier] = useState("all"); // all | high | med | low | review
  const [source, setSource] = useState("all");
  const [tag, setTag] = useState("all");
  const [sort, setSort] = useState("recent");
  const [view, setView] = useState("grid"); // grid | list

  const allTags = useMemo(() => {
    const s = new Set();
    DATA.forEach((r) => r.tags.forEach((t) => s.add(t)));
    return ["all", ...[...s].sort()];
  }, []);

  const filtered = useMemo(() => {
    let rows = DATA.slice();
    if (q.trim()) {
      const needle = q.toLowerCase();
      rows = rows.filter((r) =>
        r.title.toLowerCase().includes(needle) ||
        r.blurb.toLowerCase().includes(needle) ||
        r.tags.some((t) => t.toLowerCase().includes(needle))
      );
    }
    if (tier !== "all") {
      rows = rows.filter((r) => {
        const t = tierFor(r.confidence);
        if (tier === "review") return !r.gated;
        if (tier === "high") return t.className === "tier-high";
        if (tier === "med") return t.className === "tier-med";
        if (tier === "low") return t.className === "tier-low";
        return true;
      });
    }
    if (source !== "all") rows = rows.filter((r) => r.source_type === source);
    if (tag !== "all") rows = rows.filter((r) => r.tags.includes(tag));

    rows.sort((a, b) => {
      if (sort === "recent") return hashSlug(b.slug) - hashSlug(a.slug);
      if (sort === "confidence") return b.confidence - a.confidence;
      if (sort === "time") return a.total_min - b.total_min;
      if (sort === "title") return a.title.localeCompare(b.title);
      return 0;
    });
    return rows;
  }, [q, tier, source, tag, sort]);

  const stats = useMemo(() => {
    const total = DATA.length;
    const needsReview = DATA.filter((r) => !r.gated).length;
    const avg = (DATA.reduce((a, b) => a + b.confidence, 0) / total).toFixed(2);
    const sources = new Set(DATA.map((r) => r.source_type)).size;
    return { total, needsReview, avg, sources };
  }, []);

  const reviewQueue = useMemo(
    () => DATA.filter((r) => !r.gated).sort((a, b) => a.confidence - b.confidence),
    []
  );

  return (
    <div className="min-h-screen">
      <TopBar />
      <main className="oc-container pt-10 md:pt-14 pb-20">
        <Header stats={stats} />
        {reviewQueue.length > 0 && <ReviewQueue recipes={reviewQueue} onReviewAll={() => setTier("review")} />}
        <FilterBar
          q={q} setQ={setQ}
          tier={tier} setTier={setTier}
          source={source} setSource={setSource}
          tag={tag} setTag={setTag} allTags={allTags}
          sort={sort} setSort={setSort}
          view={view} setView={setView}
          resultCount={filtered.length}
          totalCount={DATA.length}
        />
        {filtered.length === 0 ? (
          <EmptyState onReset={() => { setQ(""); setTier("all"); setSource("all"); setTag("all"); }} />
        ) : view === "grid" ? (
          <Grid recipes={filtered} />
        ) : (
          <List recipes={filtered} />
        )}
      </main>
      <Footer />
    </div>
  );
}

// ───────────────────────────── Top Bar ─────────────────────────────

function TopBar() {
  const [open, setOpen] = useState(false);
  return (
    <header className="oc-topbar">
      <div className="oc-container flex h-14 items-center justify-between gap-3">
        <div className="flex items-center gap-8 min-w-0">
          <a className="flex items-center gap-2.5" href="Landing.html">
            <Logo />
            <span className="font-display text-[20px] leading-none text-stone-900">OneCook</span>
            <span className="ml-1 oc-pill">AI</span>
          </a>
          <nav className="hidden gap-6 text-[13px] text-stone-600 md:flex">
            <a href="Library.html" className="text-stone-900 font-medium">Library</a>
            <a href="Recipe.html" className="hover:text-stone-900">Recipe</a>
          </nav>
        </div>
        <div className="flex items-center gap-2">
          <div className="oc-avatar">BR</div>
          <button onClick={() => setOpen(!open)} className="oc-menu-btn" aria-label="Toggle menu">
            {open ? (
              <svg viewBox="0 0 16 16"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
            ) : (
              <svg viewBox="0 0 16 16"><path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
            )}
          </button>
        </div>
      </div>
      {open && (
        <nav className="oc-mobile-panel">
          <a href="Library.html">Library</a>
          <a href="Recipe.html">Example recipe <span>→</span></a>
        </nav>
      )}
    </header>
  );
}

function Logo() {
  return (
    <svg viewBox="0 0 28 28" className="size-7 text-[var(--accent)]">
      <circle cx="14" cy="14" r="12.5" fill="none" stroke="currentColor" strokeWidth="1.4"/>
      <path d="M9 11c0-2.8 2.2-5 5-5s5 2.2 5 5v3H9z" fill="currentColor" opacity="0.18"/>
      <path d="M9 11c0-2.8 2.2-5 5-5s5 2.2 5 5v3H9z M11 14v6M14 14v6M17 14v6" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/>
    </svg>
  );
}

// ───────────────────────────── Header ─────────────────────────────

function Header({ stats }) {
  return (
    <section className="oc-lib-header">
      <div className="oc-lib-header-text">
        <div className="oc-eyebrow">Library</div>
        <h1 className="oc-lib-title">Your kitchen, indexed.</h1>
        <p className="oc-lib-deck">
          Every recipe you've captured — handwritten cards, screenshots, voice memos, links —
          structured, searchable, and ranked by how sure OneCook is it got things right.
        </p>
      </div>
      <div className="oc-lib-stats">
        <StatCell n={stats.total} label="Recipes" />
        <StatCell n={stats.avg} label="Avg. confidence" mono />
        <StatCell n={stats.needsReview} label="Need review" warn={stats.needsReview > 0} />
        <StatCell n={stats.sources} label="Source types" />
      </div>
    </section>
  );
}

function StatCell({ n, label, mono, warn }) {
  return (
    <div className="oc-stat-cell">
      <div className={`oc-stat-n ${mono ? "font-mono" : "font-display"} ${warn ? "text-amber-700" : ""}`}>
        {n}
      </div>
      <div className="oc-eyebrow mt-1">{label}</div>
    </div>
  );
}

// ───────────────────────────── Review Queue ─────────────────────────────

function ReviewQueue({ recipes, onReviewAll }) {
  return (
    <section className="oc-review">
      <div className="flex items-end justify-between gap-4 mb-5">
        <div>
          <div className="oc-eyebrow text-amber-700">Capture inbox</div>
          <h2 className="oc-section-title mt-1">Needs your eye — {recipes.length}</h2>
          <p className="mt-1.5 text-[13.5px] text-stone-600 max-w-prose">
            Extractions that didn't clear the confidence gate. Open one to compare against the source and approve, edit, or reject.
          </p>
        </div>
        <button onClick={onReviewAll} className="oc-link self-end whitespace-nowrap">Review all →</button>
      </div>
      <div className="oc-review-strip">
        {recipes.map((r) => <ReviewCard key={r.slug} r={r} />)}
      </div>
    </section>
  );
}

function ReviewCard({ r }) {
  const t = tierFor(r.confidence);
  return (
    <a href={r.href || "Recipe.html"} className="oc-review-card">
      <div className="flex items-start justify-between gap-3">
        <div className="oc-eyebrow">{r.source_type}</div>
        <span className={`oc-tier-chip ${t.className}`}>
          <span className="oc-dot" /> {r.confidence.toFixed(2)}
        </span>
      </div>
      <h3 className="font-display text-[20px] leading-tight text-stone-900 mt-3 text-wrap-balance">{r.title}</h3>
      <p className="mt-2 text-[12.5px] text-stone-600 leading-snug line-clamp-2">{r.blurb}</p>
      <div className="mt-4 flex items-center justify-between border-t border-amber-200/60 pt-3">
        <span className="font-mono text-[10.5px] uppercase tracking-[0.12em] text-amber-700">
          {r.ambiguities} {r.ambiguities === 1 ? "flag" : "flags"} · iter #{r.iteration}
        </span>
        <span className="oc-link-soft">Review →</span>
      </div>
    </a>
  );
}

// ───────────────────────────── Filter Bar ─────────────────────────────

const SOURCE_OPTS = [
  ["all", "All sources", null],
  ["handwritten", "Handwritten", "pen"],
  ["photo", "Photo", "camera"],
  ["url", "URL", "link"],
  ["voice", "Voice memo", "mic"],
  ["video", "Video", "film"],
];

const TIER_OPTS = [
  ["all", "All"],
  ["high", "High confidence"],
  ["med", "Medium"],
  ["low", "Low"],
  ["review", "Needs review"],
];

function FilterBar({ q, setQ, tier, setTier, source, setSource, tag, setTag, allTags, sort, setSort, view, setView, resultCount, totalCount }) {
  return (
    <section className="oc-filter">
      <div className="oc-filter-row">
        <div className="oc-search">
          <svg viewBox="0 0 16 16" className="size-4 text-stone-400"><circle cx="7" cy="7" r="5" fill="none" stroke="currentColor" strokeWidth="1.4"/><path d="M11 11l3 3" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>
          <input
            type="text"
            value={q}
            onChange={(e) => setQ(e.target.value)}
            placeholder="Search by title, ingredient, or tag…"
          />
          {q && <button onClick={() => setQ("")} className="oc-search-clear">×</button>}
        </div>
        <div className="oc-view-toggle">
          <button onClick={() => setView("grid")} className={view === "grid" ? "is-active" : ""} title="Grid">
            <svg viewBox="0 0 16 16" className="size-3.5"><rect x="2" y="2" width="5" height="5" fill="none" stroke="currentColor" strokeWidth="1.3"/><rect x="9" y="2" width="5" height="5" fill="none" stroke="currentColor" strokeWidth="1.3"/><rect x="2" y="9" width="5" height="5" fill="none" stroke="currentColor" strokeWidth="1.3"/><rect x="9" y="9" width="5" height="5" fill="none" stroke="currentColor" strokeWidth="1.3"/></svg>
          </button>
          <button onClick={() => setView("list")} className={view === "list" ? "is-active" : ""} title="List">
            <svg viewBox="0 0 16 16" className="size-3.5"><path d="M3 4h10M3 8h10M3 12h10" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>
          </button>
        </div>
        <select value={sort} onChange={(e) => setSort(e.target.value)} className="oc-select">
          <option value="recent">Recent</option>
          <option value="confidence">Highest confidence</option>
          <option value="time">Quickest first</option>
          <option value="title">A → Z</option>
        </select>
      </div>

      <div className="oc-filter-chips">
        <ChipGroup label="Confidence">
          {TIER_OPTS.map(([id, label]) => (
            <Chip
              key={id}
              active={tier === id}
              onClick={() => setTier(id)}
              dot={id === "high" ? "var(--tier-high)" : id === "med" ? "var(--tier-med)" : id === "low" || id === "review" ? "var(--tier-low)" : null}
            >{label}</Chip>
          ))}
        </ChipGroup>
        <ChipGroup label="Source">
          {SOURCE_OPTS.map(([id, label, icon]) => (
            <Chip key={id} active={source === id} onClick={() => setSource(id)} icon={icon}>{label}</Chip>
          ))}
        </ChipGroup>
        <ChipGroup label="Tag">
          <select value={tag} onChange={(e) => setTag(e.target.value)} className="oc-select oc-select-sm">
            {allTags.map((t) => <option key={t} value={t}>{t === "all" ? "All tags" : t}</option>)}
          </select>
        </ChipGroup>
      </div>

      <div className="oc-filter-result">
        <span className="font-mono text-[11px] uppercase tracking-[0.12em] text-stone-500">
          Showing {resultCount} of {totalCount}
        </span>
      </div>
    </section>
  );
}

function ChipGroup({ label, children }) {
  return (
    <div className="oc-chip-group">
      <span className="oc-chip-group-label">{label}</span>
      <div className="oc-chip-group-items">{children}</div>
    </div>
  );
}

function Chip({ active, onClick, children, dot, icon }) {
  return (
    <button onClick={onClick} className={`oc-filter-chip ${active ? "is-active" : ""}`}>
      {dot && <span className="size-1.5 rounded-full" style={{ background: dot }} />}
      {icon && <SourceIcon kind={icon} />}
      {children}
    </button>
  );
}

function SourceIcon({ kind }) {
  const map = {
    pen: <path d="M3 13l3-1L13 5l-2-2-7 7-1 3z" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/>,
    camera: <><path d="M2 5h2l1-2h6l1 2h2v8H2z" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round"/><circle cx="8" cy="9" r="2.4" fill="none" stroke="currentColor" strokeWidth="1.3"/></>,
    link: <path d="M6 10l4-4 M5 7L3 9a2 2 0 002.8 2.8L8 9.8 M8 6l2-2a2 2 0 012.8 2.8L11 8.8" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/>,
    mic: <><rect x="6" y="2" width="4" height="8" rx="2" fill="none" stroke="currentColor" strokeWidth="1.3"/><path d="M3 8a5 5 0 0010 0 M8 13v2" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></>,
    film: <><rect x="2" y="3" width="12" height="10" fill="none" stroke="currentColor" strokeWidth="1.3"/><path d="M4 3v10M12 3v10M2 6h2M2 10h2M12 6h2M12 10h2" stroke="currentColor" strokeWidth="1.1"/></>,
  };
  return <svg viewBox="0 0 16 16" className="size-3">{map[kind]}</svg>;
}

// ───────────────────────────── Grid ─────────────────────────────

function Grid({ recipes }) {
  return (
    <div className="oc-grid">
      {recipes.map((r) => <Card key={r.slug} r={r} />)}
    </div>
  );
}

function Card({ r }) {
  const t = tierFor(r.confidence);
  const iconKind = { handwritten: "pen", photo: "camera", url: "link", voice: "mic", video: "film" }[r.source_type];
  return (
    <a href={r.href || "Recipe.html"} className="oc-card">
      <div className="oc-card-thumb" style={{ "--c1": r.color[0], "--c2": r.color[1] }}>
        <div className="oc-card-thumb-glow" />
        <div className="oc-card-thumb-grid" />
        <div className="oc-thumb-source">
          <SourceIcon kind={iconKind} />
          <span>{r.source_type}</span>
        </div>
        <div className={`oc-thumb-conf ${t.className}`}>
          <span className="oc-dot" />
          <span className="font-mono tabular-nums">{r.confidence.toFixed(2)}</span>
        </div>
        {!r.gated && <div className="oc-thumb-review">Review</div>}
      </div>
      <div className="oc-card-body">
        <h3 className="oc-card-title">{r.title}</h3>
        <p className="oc-card-blurb">{r.blurb}</p>
        <div className="oc-card-meta">
          <span><DotIcon kind="clock" /> {r.cook_time}</span>
          <span><DotIcon kind="serve" /> Serves {r.servings}</span>
          <span><DotIcon kind="diff" /> {r.difficulty}</span>
        </div>
        <div className="oc-card-tags">
          {r.tags.slice(0, 3).map((tag) => (
            <span key={tag} className="oc-tag">{tag}</span>
          ))}
        </div>
      </div>
    </a>
  );
}

function DotIcon({ kind }) {
  const map = {
    clock: <><circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" strokeWidth="1.3"/><path d="M8 4.5V8l2.5 1.5" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></>,
    serve: <><path d="M3 6h10 M3 6a5 5 0 0010 0 M8 11v2 M5 14h6" fill="none" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round"/></>,
    diff: <><circle cx="3.5" cy="11" r="1.2" fill="currentColor"/><circle cx="8" cy="11" r="1.2" fill="currentColor"/><circle cx="12.5" cy="11" r="1.2" fill="currentColor" opacity="0.3"/></>,
  };
  return <svg viewBox="0 0 16 16" className="size-3 inline-block mr-1.5 align-[-2px]">{map[kind]}</svg>;
}

// ───────────────────────────── List view ─────────────────────────────

function List({ recipes }) {
  return (
    <div className="oc-list">
      <div className="oc-list-head">
        <div>Recipe</div>
        <div>Source</div>
        <div>Time</div>
        <div>Serves</div>
        <div>Confidence</div>
      </div>
      {recipes.map((r) => <Row key={r.slug} r={r} />)}
    </div>
  );
}

function Row({ r }) {
  const t = tierFor(r.confidence);
  const iconKind = { handwritten: "pen", photo: "camera", url: "link", voice: "mic", video: "film" }[r.source_type];
  return (
    <a href={r.href || "Recipe.html"} className="oc-row">
      <div className="oc-row-title">
        <div className="oc-row-swatch" style={{ "--c1": r.color[0], "--c2": r.color[1] }} />
        <div>
          <div className="font-display text-[18px] leading-tight text-stone-900">{r.title}</div>
          <div className="text-[12.5px] text-stone-500 line-clamp-1">{r.blurb}</div>
        </div>
      </div>
      <div className="text-[13px] text-stone-700 inline-flex items-center gap-1.5"><SourceIcon kind={iconKind} /> {r.source_type}</div>
      <div className="text-[13px] text-stone-700">{r.cook_time}</div>
      <div className="text-[13px] text-stone-700">{r.servings}</div>
      <div className="flex items-center gap-2">
        <span className={`oc-tier-chip ${t.className}`}>
          <span className="oc-dot" />
          <span className="font-mono tabular-nums">{r.confidence.toFixed(2)}</span>
        </span>
        {!r.gated && <span className="oc-mini-flag">review</span>}
      </div>
    </a>
  );
}

// ───────────────────────────── Empty & Footer ─────────────────────────────

function EmptyState({ onReset }) {
  return (
    <div className="oc-empty">
      <div className="oc-empty-illo">∅</div>
      <h3 className="font-display text-[24px] mt-4 text-stone-900">No recipes match.</h3>
      <p className="mt-2 text-[14px] text-stone-600">Try widening the confidence filter, or clear the search.</p>
      <button onClick={onReset} className="oc-btn-secondary mt-5">Reset filters</button>
    </div>
  );
}

function Footer() {
  return (
    <footer className="oc-footer oc-container">
      <div className="flex items-center gap-2">
        <Logo /> <span className="font-display text-[15px] text-stone-700">OneCook</span>
        <span className="font-mono text-[11px] uppercase tracking-[0.1em] text-stone-400">
          · Recipes you've actually cooked
        </span>
      </div>
      <div className="font-mono text-[11px] uppercase tracking-[0.1em] text-stone-400">
        v3.2.1 · build a7f9d2
      </div>
    </footer>
  );
}

// ───────────────────────────── Mount ─────────────────────────────

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