// cookbook.js — Cookbook 2.0 (v4): Use-Case-Setups („Wofür?") + Profi-Suche.
// Backend: /api/cookbook/{recipes,install-recipe,analyze,evaluate} (hw_math).

import { api, getHfToken } from "../core/api.js";
import { $, esc, icon, toast, infoDot } from "../core/ui.js";

const CTX_HELP = "Kontext = das Kurzzeitgedächtnis des Modells. Größer = merkt sich mehr, braucht aber mehr Speicher und wird etwas langsamer.";
const GGUF_HELP = "GGUF ist das lokal lauffähige Dateiformat. Unsere Engine lädt nur GGUF — Repos ohne GGUF-Datei kann sie nicht nutzen.";

const FILTERS = [
  { id: "", label: "Alle" }, { id: "coder", label: "Coder" }, { id: "scout", label: "Scout" },
  { id: "vision", label: "Vision" }, { id: "manager", label: "Manager" }, { id: "reviewer", label: "Reviewer" },
];

let lastSys = null;
let currentResults = [];
let currentAnalysis = null;
let activeFilter = "";
let RECIPES = [];
let RECOMMENDED = null;

const fitCls = lvl => lvl === "perfect" ? "ok" : lvl === "marginal" ? "warn" : "bad";
const fitWord = lvl => lvl === "perfect" ? "Passt locker" : lvl === "marginal" ? "Wird knapp" : "Zu groß";

function mount() {
  const c = $(".view[data-view='cookbook']");
  c.innerHTML = `
    <div class="pagehead"><div>
      <h1>Modell-Cookbook</h1>
      <div class="sub">Wofür willst du es nutzen? Wir empfehlen das passende Setup für deine Hardware.</div>
    </div></div>

    <div class="card-h" style="align-items:center"><h3>Wofür möchtest du es nutzen?</h3>
      <span class="chip" id="cb-hw">deine Hardware</span></div>
    <div class="grid grid-3" id="cb-recipes">
      <div class="empty" style="grid-column:1/-1;text-align:center">Lade Setups…</div>
    </div>

    <details class="guide-acc card" style="padding:0;overflow:hidden;margin-top:8px">
      <summary>Profi-Modus: HuggingFace direkt durchsuchen</summary>
      <div class="acc-body">
        <div class="flex gap-3" style="margin-bottom:8px">
          <input id="cb-search" placeholder="Modell suchen, z.B. „Llama 3"…" style="margin:0;flex:1">
          <button class="primary" id="cb-btn-search" style="white-space:nowrap">Suchen</button>
        </div>
        <div class="flex gap-2 items-center" style="flex-wrap:wrap">
          <div id="cb-filters" class="flex gap-2" style="flex-wrap:wrap;flex:1"></div>
          <select id="cb-sort" style="width:auto;margin:0;padding:7px 10px;font-family:var(--sans)">
            <option value="downloads">Beliebteste Downloads</option>
            <option value="likes">Am beliebtesten (Likes)</option>
            <option value="lastModified">Zuletzt aktualisiert</option>
            <option value="trendingScore">Im Trend</option>
          </select>
        </div>
        <div class="legend" style="margin:14px 0">
          <span><i style="background:var(--on)"></i>passt locker</span>
          <span><i style="background:var(--warn)"></i>läuft, aber knapp</span>
          <span><i style="background:var(--err)"></i>zu groß für deinen Speicher</span>
        </div>
        <div class="hint" style="margin:-6px 0 14px">Es werden nur Modelle im GGUF-Format ${infoDot(GGUF_HELP)} gezeigt — nur die laufen lokal.</div>
        <div class="grid grid-3" id="cb-grid"></div>
      </div>
    </details>

    <div id="cb-recipe-modal" class="modal-overlay" style="display:none">
      <div class="modal-card" style="max-width:580px">
        <button id="cb-r-close" class="ghost" style="position:absolute;top:14px;right:14px">Schließen</button>
        <h3 id="cb-r-title">Setup</h3>
        <p class="text-mut text-sm" id="cb-r-desc" style="margin:-4px 0 16px"></p>
        <div id="cb-r-models"></div>
        <div class="hint" id="cb-r-note" style="margin-top:12px"></div>
        <button class="primary" id="cb-r-install" style="width:100%;margin-top:8px">Komplettes Setup installieren</button>
        <div class="hint" style="margin-top:8px">Lädt alle Modelle herunter und pflegt sie mit optimalem Kontext ein. Fortschritt in der Aktivität.</div>
      </div>
    </div>

    <div id="cb-modal" class="modal-overlay" style="display:none">
      <div class="modal-card" style="max-width:560px">
        <button id="cb-modal-close" class="ghost" style="position:absolute;top:14px;right:14px">Schließen</button>
        <h3 id="cb-m-title">Modell</h3>
        <p class="mono-sm" id="cb-m-repo" style="margin:-4px 0 16px">repo/name</p>
        <label>Quantisierung (GGUF-Datei) wählen</label>
        <select id="cb-m-files"></select>
        <div class="hint" id="cb-m-loading" style="display:none">Lade Dateien von HuggingFace…</div>
        <div class="row" style="margin-top:4px">
          <div><label>Alias (Rolle)</label><input id="cb-m-alias" placeholder="z.B. coder"></div>
          <div><label>Kontext-Größe ${infoDot(CTX_HELP)}</label><input id="cb-m-ctx" type="number" value="8192"></div>
        </div>
        <div class="hint" id="cb-m-ctx-rec" style="margin:-6px 0 14px"></div>
        <div id="cb-m-fit" class="tile" style="display:flex;justify-content:space-between;align-items:center;margin:8px 0 18px">
          <div><div style="font-size:13px">Ressourcen-Check</div>
            <div class="hint" id="cb-m-fit-text" style="margin:4px 0 0">Berechne…</div></div>
          <span id="cb-m-fit-badge"></span>
        </div>
        <button class="primary" id="cb-m-download" style="width:100%">Herunterladen &amp; Einpflegen</button>
      </div>
    </div>`;

  renderFilters();
  $("#cb-btn-search").addEventListener("click", doSearch);
  $("#cb-search").addEventListener("keydown", e => { if (e.key === "Enter") doSearch(); });
  $("#cb-sort").addEventListener("change", () => { if ($("#cb-search").value.trim() || activeFilter) doSearch(); });
  $("#cb-modal-close").addEventListener("click", () => $("#cb-modal").style.display = "none");
  $("#cb-modal").addEventListener("click", e => { if (e.target.id === "cb-modal") $("#cb-modal").style.display = "none"; });
  $("#cb-m-download").addEventListener("click", doDownload);
  $("#cb-m-files").addEventListener("change", updateLiveFit);
  $("#cb-m-ctx").addEventListener("change", reanalyzeCtx);
  window.cbSetCtx = v => { $("#cb-m-ctx").value = v; reanalyzeCtx(); };
  $("#cb-r-close").addEventListener("click", () => $("#cb-recipe-modal").style.display = "none");
  $("#cb-recipe-modal").addEventListener("click", e => { if (e.target.id === "cb-recipe-modal") $("#cb-recipe-modal").style.display = "none"; });

  renderHwChip();
  loadRecipes();
}

// ---- Use-Case-Setups ----
async function loadRecipes() {
  try {
    const d = await api("/api/cookbook/recipes");
    RECIPES = d.recipes || [];
    RECOMMENDED = d.recommended_id || null;
    renderRecipes();
  } catch (e) {
    $("#cb-recipes").innerHTML = `<div class="alert err" style="grid-column:1/-1">Setups nicht ladbar: ${esc(e.message)}</div>`;
  }
}

function renderRecipes() {
  $("#cb-recipes").innerHTML = RECIPES.map(r => {
    const best = r.id === RECOMMENDED;
    return `<button class="card-btn${best ? " cb-best" : ""}" data-recipe="${esc(r.id)}">
      ${best ? `<div class="cb-best-tag">★ Beste Wahl für dein System</div>` : ""}
      <div class="flex justify-between items-center">
        <span class="flex items-center gap-2"><span class="text-accent">${icon(r.icon)}</span>
          <h3 style="margin:0;font-size:15px">${esc(r.title)}</h3></span>
        <span class="fit-badge ${fitCls(r.fit_level)}">${fitWord(r.fit_level)}</span>
      </div>
      <p>${esc(r.desc)}</p>
      <div class="text-xs text-mut">${r.models.length} Modell${r.models.length > 1 ? "e" : ""} im Setup</div>
    </button>`;
  }).join("");
  $("#cb-recipes").querySelectorAll("[data-recipe]").forEach(b =>
    b.addEventListener("click", () => openRecipe(b.getAttribute("data-recipe"))));
}

function openRecipe(id) {
  const r = RECIPES.find(x => x.id === id); if (!r) return;
  $("#cb-r-title").textContent = r.title;
  $("#cb-r-desc").textContent = r.desc;
  $("#cb-r-models").innerHTML = `<div class="list">${r.models.map(m => `
    <div class="li" style="align-items:flex-start">
      <div class="li-main">
        <div class="flex items-center gap-2" style="flex-wrap:wrap">
          <span class="li-id">${esc(m.name)}</span><span class="tag text">${esc(m.role)}</span></div>
        <div class="li-sub">${esc(m.why)}</div>
        <div class="li-sub mono-sm">~${m.fit.req_gb.toFixed(1)} GB · ~${Math.round(m.fit.tps)} Tok/s · optimal ~${Math.round(m.optimal_ctx / 1024)}k Kontext</div>
      </div>
      <span class="fit-badge ${fitCls(m.fit.level)}">${esc(m.fit.text)}</span>
    </div>`).join("")}</div>`;
  const maxRam = Math.max(...r.models.map(m => m.fit.req_gb));
  $("#cb-r-note").innerHTML = `Größter Spitzenbedarf: <b>~${maxRam.toFixed(1)} GB</b>. ` +
    `Es läuft immer nur <b>ein</b> Modell gleichzeitig — das größte bestimmt, ob das Setup passt (kein Summieren).`;
  const btn = $("#cb-r-install");
  btn.className = r.fit_level === "too_tight" ? "primary warn" : "primary";
  btn.textContent = r.fit_level === "too_tight" ? "Trotzdem installieren (zu groß)" : "Komplettes Setup installieren";
  btn.onclick = () => installRecipe(r.id);
  $("#cb-recipe-modal").style.display = "flex";
}

async function installRecipe(id) {
  const btn = $("#cb-r-install"); btn.disabled = true; btn.textContent = "Starte…";
  try {
    const r = await api("/api/cookbook/install-recipe", { method: "POST", body: JSON.stringify({ recipe_id: id, hf_token: getHfToken() }) });
    toast(`Setup wird installiert (${r.count} Modelle) — siehe Aktivität.`);
    $("#cb-recipe-modal").style.display = "none";
    document.querySelector(".nav-item[data-view='activity']")?.click();
  } catch (e) { toast("Fehler: " + e.message, true); }
  btn.disabled = false; btn.textContent = "Komplettes Setup installieren";
}

// ---- Hardware-Chip / Filter ----
function renderFilters() {
  $("#cb-filters").innerHTML = FILTERS.map(f =>
    `<button class="${activeFilter === f.id ? "primary" : "ghost"}" data-f="${f.id}" style="border-radius:999px">${f.label}</button>`).join("");
  $("#cb-filters").querySelectorAll("[data-f]").forEach(b =>
    b.addEventListener("click", () => { activeFilter = b.getAttribute("data-f"); renderFilters(); doSearch(); }));
}
function renderHwChip() {
  const el = $("#cb-hw"); if (!el) return;
  if (lastSys?.ram?.total) el.innerHTML = `${icon("cpu")}${Math.round(lastSys.ram.total / 1024 ** 3)} GB Speicher`;
}

// ---- Profi-Suche (HuggingFace) ----
function metricLine(fit) { return `~${fit.req_gb.toFixed(1)} GB · ~${Math.round(fit.tps)} Tok/s`; }

async function doSearch() {
  let q = $("#cb-search").value.trim();
  if (activeFilter) q = q ? q + " " + activeFilter : activeFilter;
  if (!q) { $("#cb-grid").innerHTML = ""; return; }
  const sort = $("#cb-sort")?.value || "downloads";
  const btn = $("#cb-btn-search"); btn.disabled = true; btn.textContent = "Lade…";
  $("#cb-grid").innerHTML = `<div class="empty" style="grid-column:1/-1;text-align:center">Suche auf HuggingFace…</div>`;
  try {
    const url = `https://huggingface.co/api/models?search=${encodeURIComponent(q)}&filter=gguf&sort=${encodeURIComponent(sort)}&direction=-1&limit=12`;
    const r = await fetch(url);
    currentResults = await r.json();
    renderResults(currentResults);
  } catch (e) { $("#cb-grid").innerHTML = `<div class="alert err" style="grid-column:1/-1">${esc(e.message)}</div>`; }
  btn.disabled = false; btn.textContent = "Suchen";
}

function renderResults(results) {
  const grid = $("#cb-grid");
  if (!results?.length) { grid.innerHTML = `<div class="empty" style="grid-column:1/-1;text-align:center">Keine GGUF-Modelle gefunden.</div>`; return; }
  grid.innerHTML = results.map((m, i) => `
    <div class="card" style="display:flex;flex-direction:column;cursor:pointer" data-res="${i}">
      <div class="flex justify-between" style="align-items:flex-start;gap:8px">
        <div style="min-width:0"><h3 style="margin:0;font-size:14.5px;font-weight:500;word-break:break-word">${esc(m.id.split("/").pop())}</h3>
          <div class="text-xs text-mut" style="margin-top:3px">${esc(m.author || "")}</div></div>
        <span class="fit-badge warn" id="cb-b-${i}">prüfe…</span>
      </div>
      <div style="flex:1;margin-top:12px"></div>
      <div class="flex justify-between items-center text-xs text-mut" style="border-top:1px solid var(--line);padding-top:11px">
        <span class="mono-sm" id="cb-m-${i}">Hardware-Fit…</span>
        <span class="mono-sm">⬇ ${(m.downloads || 0).toLocaleString()}</span></div>
    </div>`).join("");
  grid.querySelectorAll("[data-res]").forEach(el => el.addEventListener("click", () => openResult(+el.getAttribute("data-res"))));
  results.forEach((m, i) => fetchFitForCard(i, m.id));
}

async function fetchFitForCard(i, repo_id) {
  try {
    const res = await api("/api/cookbook/analyze", { method: "POST", body: JSON.stringify({ repo_id, ctx: 8192 }) });
    const b = $("#cb-b-" + i), mt = $("#cb-m-" + i);
    if (!b || !mt) return;
    if (!res.files?.length) { b.className = "fit-badge"; b.style.cssText = "background:rgba(139,151,165,.14);color:var(--mut)"; b.title = GGUF_HELP; b.textContent = "kein GGUF"; mt.textContent = "—"; return; }
    let best = res.files.find(f => f.quant?.includes("Q4_K_M")) || res.files[0];
    b.className = "fit-badge " + fitCls(best.fit.level); b.textContent = best.fit.text;
    mt.textContent = metricLine(best.fit) + " · " + (best.quant || "GGUF");
  } catch { const b = $("#cb-b-" + i); if (b) { b.className = "fit-badge bad"; b.textContent = "Fehler"; } }
}

function showFit() {
  const file = $("#cb-m-files").value;
  const f = currentAnalysis?.files.find(x => x.filename === file);
  if (!f) { $("#cb-m-fit").style.display = "none"; return; }
  $("#cb-m-fit").style.display = "flex";
  $("#cb-m-fit-text").innerHTML = `Bedarf: <b>~${f.fit.req_gb.toFixed(1)} GB</b> · ${currentAnalysis.params_b}B · ${esc(f.quant)} · ~${Math.round(f.fit.tps)} Tok/s`;
  $("#cb-m-fit-badge").innerHTML = `<span class="fit-badge ${fitCls(f.fit.level)}">${esc(f.fit.text)}</span>`;
  const opt = f.optimal_ctx, recEl = $("#cb-m-ctx-rec");
  if (recEl) recEl.innerHTML = opt
    ? `Empfohlener Kontext für deine Hardware: <b>~${Math.round(opt / 1024)}k</b> — <a href="#" onclick="event.preventDefault();window.cbSetCtx(${opt})">übernehmen</a>` : "";
  const btn = $("#cb-m-download");
  if (f.fit.level === "too_tight") { btn.className = "primary warn"; btn.textContent = "Trotzdem holen (zu groß)"; }
  else { btn.className = "primary"; btn.textContent = "Herunterladen & Einpflegen"; }
}
function updateLiveFit() { showFit(); }

async function reanalyzeCtx() {
  if (!currentAnalysis) return;
  const ctx = parseInt($("#cb-m-ctx").value) || 8192;
  const file = $("#cb-m-files").value;
  try {
    currentAnalysis = await api("/api/cookbook/analyze", { method: "POST", body: JSON.stringify({ repo_id: currentAnalysis.repo, ctx }) });
    $("#cb-m-files").value = file; showFit();
  } catch {}
}

function openModalBase(title, repo) {
  $("#cb-modal").style.display = "flex";
  $("#cb-m-title").textContent = title;
  $("#cb-m-repo").textContent = repo;
}

async function openResult(i) {
  const m = currentResults[i]; if (!m) return;
  openModalBase(m.id.split("/").pop(), m.id);
  $("#cb-m-alias").value = m.id.split("/").pop().toLowerCase().replace(/[^a-z0-9]/g, "-");
  $("#cb-m-files").style.display = "none"; $("#cb-m-loading").style.display = "block"; $("#cb-m-download").disabled = true;
  try {
    const ctx = parseInt($("#cb-m-ctx").value) || 8192;
    currentAnalysis = await api("/api/cookbook/analyze", { method: "POST", body: JSON.stringify({ repo_id: m.id, ctx }) });
    $("#cb-m-loading").style.display = "none"; $("#cb-m-files").style.display = "block";
    if (!currentAnalysis.files?.length) { $("#cb-m-files").innerHTML = "<option>Keine GGUF-Dateien gefunden</option>"; $("#cb-m-fit").style.display = "none"; return; }
    $("#cb-m-files").innerHTML = currentAnalysis.files.map(f => {
      const mark = f.fit.level === "perfect" ? "●" : f.fit.level === "marginal" ? "◐" : "○";
      return `<option value="${esc(f.filename)}">${mark} ${esc(f.filename)}</option>`;
    }).join("");
    $("#cb-m-download").disabled = false; showFit();
  } catch (e) { $("#cb-m-loading").textContent = "Fehler: " + e.message; }
}

async function doDownload() {
  const repo = $("#cb-m-repo").textContent, file = $("#cb-m-files").value;
  const alias = $("#cb-m-alias").value.trim(), ctx = parseInt($("#cb-m-ctx").value) || 8192;
  if (!repo || !file || !alias) return toast("Bitte alle Felder ausfüllen.", true);
  const btn = $("#cb-m-download"); btn.disabled = true; btn.textContent = "Starte…";
  try {
    const res = await api("/api/download", { method: "POST", body: JSON.stringify({ repo, file, hf_token: getHfToken() }) });
    await api("/api/register", { method: "POST", body: JSON.stringify({ alias, model_path: res.expected_path, ctx }) });
    toast("Download gestartet — siehe Aktivität.");
    $("#cb-modal").style.display = "none";
    document.querySelector(".nav-item[data-view='activity']")?.click();
  } catch (e) { toast("Fehler: " + e.message, true); }
  btn.disabled = false; btn.textContent = "Herunterladen & Einpflegen";
}

function onSystem(sys) { lastSys = sys; renderHwChip(); }

export default { id: "cookbook", mount, onSystem };
