"use client";
// app/settings/page.jsx — choose and configure your AI provider, all stored locally.
// Flush two-column "paper form": sticky table of contents + underline-only fields.
import { useEffect, useRef, useState } from "react";
import {
  IconUserCircle, IconCpu, IconSchool, IconAdjustments,
  IconPhoto, IconRefresh, IconPlug, IconCheck, IconX,
} from "@tabler/icons-react";

const PROVIDERS = [
  { id: "ollama", name: "Ollama", desc: "Free, private, runs on this computer", local: true },
  { id: "lmstudio", name: "LM Studio", desc: "Free, private, runs on this computer", local: true },
  { id: "anthropic", name: "Anthropic (Claude)", desc: "Cloud API — needs an API key", local: false },
  { id: "openai", name: "OpenAI (GPT)", desc: "Cloud API — needs an API key", local: false },
  { id: "google", name: "Google AI (Gemini)", desc: "Cloud API — needs an API key", local: false },
];
const KEY_LINKS = {
  anthropic: "https://console.anthropic.com/",
  openai: "https://platform.openai.com/api-keys",
  google: "https://aistudio.google.com/apikey",
};
const TOC = [
  { id: "teacher", label: "Teacher & school", icon: IconUserCircle },
  { id: "provider", label: "AI provider", icon: IconCpu },
  { id: "canvas", label: "Canvas (LMS)", icon: IconSchool },
  { id: "generation", label: "Generation defaults", icon: IconAdjustments },
];

export default function SettingsPage() {
  const [s, setS] = useState(null);
  const [models, setModels] = useState({});
  const [modelsBusy, setModelsBusy] = useState("");
  const [modelsErr, setModelsErr] = useState({});
  const [test, setTest] = useState({});
  const [canvasTest, setCanvasTest] = useState(null);
  const [autoInfo, setAutoInfo] = useState(null);
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState("");
  const [error, setError] = useState("");
  const [activeToc, setActiveToc] = useState("teacher");
  const toastTimer = useRef(null);

  useEffect(() => {
    fetch("/api/settings").then((r) => r.json()).then(setS).catch(() => setError("Could not load settings."));
  }, []);

  const activeProvider = s?.provider;
  const activeModel = s?.providers?.[activeProvider]?.model || "";
  const activeKey = s?.providers?.[activeProvider]?.apiKey || "";
  const autoOn = s ? s.generation?.auto !== false : true;

  useEffect(() => {
    if (!s || !autoOn || !activeModel) { setAutoInfo(null); return; }
    let cancelled = false;
    setAutoInfo(null);
    const t = setTimeout(() => {
      fetch("/api/providers", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "defaults", provider: activeProvider, settings: s }) })
        .then((r) => r.json())
        .then((d) => { if (!cancelled && !d.error && d.maxTokens) setAutoInfo(d); })
        .catch(() => {});
    }, 500);
    return () => { cancelled = true; clearTimeout(t); };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeProvider, activeModel, activeKey, autoOn]);

  // Highlight the TOC entry for the section currently in view.
  useEffect(() => {
    if (!s) return;
    const obs = new IntersectionObserver(
      (entries) => { entries.forEach((e) => { if (e.isIntersecting) setActiveToc(e.target.id); }); },
      { rootMargin: "-20% 0px -70% 0px", threshold: 0 }
    );
    TOC.forEach(({ id }) => { const el = document.getElementById(id); if (el) obs.observe(el); });
    return () => obs.disconnect();
  }, [s]);

  function showToast(msg) { setToast(msg); clearTimeout(toastTimer.current); toastTimer.current = setTimeout(() => setToast(""), 2400); }
  function setProviderField(provider, field, value) { setS((cur) => ({ ...cur, providers: { ...cur.providers, [provider]: { ...cur.providers[provider], [field]: value } } })); }
  function setGen(field, value) { setS((cur) => ({ ...cur, generation: { ...cur.generation, [field]: value } })); }
  function setProfile(field, value) { setS((cur) => ({ ...cur, profile: { ...(cur.profile || {}), [field]: value } })); }
  function setCanvas(field, value) { setCanvasTest(null); setS((cur) => ({ ...cur, canvas: { ...(cur.canvas || {}), [field]: value } })); }

  async function testCanvas() {
    setCanvasTest({ busy: true });
    try {
      const res = await fetch("/api/canvas", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "test", settings: s }) });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Test failed.");
      setCanvasTest({ ok: true, message: data.message });
    } catch (e) { setCanvasTest({ ok: false, message: String(e.message || e) }); }
  }

  function onLogoFile(e) {
    const file = e.target.files?.[0];
    e.target.value = "";
    if (!file) return;
    if (!file.type.startsWith("image/")) { setError("Please choose an image file (PNG, JPG, etc.)."); return; }
    const url = URL.createObjectURL(file);
    const img = new Image();
    img.onload = () => {
      URL.revokeObjectURL(url);
      const scale = Math.min(1, 512 / Math.max(img.width, img.height));
      const canvas = document.createElement("canvas");
      canvas.width = Math.max(1, Math.round(img.width * scale));
      canvas.height = Math.max(1, Math.round(img.height * scale));
      canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height);
      setProfile("logo", canvas.toDataURL("image/png"));
    };
    img.onerror = () => { URL.revokeObjectURL(url); setError("Couldn't read that image — try a PNG or JPG."); };
    img.src = url;
  }

  async function refreshModels(provider) {
    setModelsBusy(provider); setModelsErr((e) => ({ ...e, [provider]: "" }));
    try {
      const res = await fetch("/api/providers", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "models", provider, settings: s }) });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Could not list models.");
      setModels((m) => ({ ...m, [provider]: data.models }));
      if (data.models.length && !data.models.includes(s.providers[provider].model)) setProviderField(provider, "model", data.models[0]);
    } catch (e) { setModelsErr((er) => ({ ...er, [provider]: String(e.message || e) })); }
    finally { setModelsBusy(""); }
  }

  async function testConnection(provider) {
    setTest((t) => ({ ...t, [provider]: { busy: true } }));
    try {
      const res = await fetch("/api/providers", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "test", provider, settings: s }) });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Test failed.");
      setTest((t) => ({ ...t, [provider]: { ok: true, message: data.message } }));
    } catch (e) { setTest((t) => ({ ...t, [provider]: { ok: false, message: String(e.message || e) } })); }
  }

  async function save() {
    setSaving(true); setError("");
    try {
      const res = await fetch("/api/settings", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(s) });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || "Save failed.");
      setS(data); showToast("Settings saved");
    } catch (e) { setError(String(e.message || e)); }
    finally { setSaving(false); }
  }

  if (!s) return <div className="page page-narrow"><p className="muted"><span className="spinner" /> Loading…</p></div>;

  const active = s.provider;
  const activeCfg = s.providers[active] || {};
  const isLocal = active === "ollama" || active === "lmstudio";
  const modelList = models[active] || [];
  const t = test[active];

  return (
    <div className="page page-narrow">
      <div className="page-head">
        <h1>Settings</h1>
        <p>Pick the AI that powers generation. Local options keep everything — source material, questions, API traffic — on this computer. Keys and settings are stored only in your local <code>data/db.json</code> file.</p>
      </div>

      {error && <div className="alert alert-error">{error}</div>}

      <div className="settings-grid">
        <nav className="settings-toc" aria-label="Settings sections">
          <span className="field-label" style={{ padding: "0 12px 8px" }}>On this page</span>
          {TOC.map(({ id, label, icon: Icon }) => (
            <a key={id} href={`#${id}`} className={`toc-link${activeToc === id ? " active" : ""}`}><Icon size={16} /> {label}</a>
          ))}
        </nav>

        <div className="settings-body">
          {/* ---- Teacher & school ---- */}
          <section className="sec" id="teacher">
            <h2>Teacher &amp; school</h2>
            <p className="sec-hint">Shown in the header of every printed and exported assignment — leave anything blank to omit it.</p>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(180px, 1fr))", gap: 26, marginBottom: 26 }}>
              <div className="ul-field" style={{ marginBottom: 0 }}>
                <span className="field-label">Teacher name</span>
                <input className="ul-input" type="text" value={s.profile?.teacherName || ""} onChange={(e) => setProfile("teacherName", e.target.value)} placeholder="e.g. Mr. Drew" />
              </div>
              <div className="ul-field" style={{ marginBottom: 0 }}>
                <span className="field-label">Class / course</span>
                <input className="ul-input" type="text" value={s.profile?.className || ""} onChange={(e) => setProfile("className", e.target.value)} placeholder="e.g. 7th Grade Science — Period 3" />
              </div>
              <div className="ul-field" style={{ marginBottom: 0 }}>
                <span className="field-label">School name</span>
                <input className="ul-input" type="text" value={s.profile?.schoolName || ""} onChange={(e) => setProfile("schoolName", e.target.value)} placeholder="e.g. Lincoln Middle School" />
              </div>
            </div>
            <span className="field-label">School logo or mascot <span className="faint" style={{ textTransform: "none", letterSpacing: 0, fontWeight: 400 }}>(optional)</span></span>
            <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap", marginTop: 6 }}>
              {s.profile?.logo && (
                <img src={s.profile.logo} alt="School logo preview" style={{ height: 52, maxWidth: 140, objectFit: "contain", background: "#fff", border: "1px solid var(--line)", borderRadius: 6, padding: 4 }} />
              )}
              <label className="btn" style={{ cursor: "pointer" }}>
                <IconPhoto size={16} /> {s.profile?.logo ? "Replace image" : "Upload image"}
                <input type="file" accept="image/*" onChange={onLogoFile} style={{ display: "none" }} />
              </label>
              {s.profile?.logo && <button className="btn btn-ghost btn-danger" style={{ borderColor: "transparent" }} onClick={() => setProfile("logo", "")}>Remove</button>}
            </div>
            <span className="field-hint">Appears beside the school name on printed pages. PNG with transparency looks best; the image is stored locally and shrunk automatically.</span>
          </section>

          {/* ---- AI provider ---- */}
          <section className="sec" id="provider">
            <h2>AI provider</h2>
            <div className="provider-list" style={{ marginTop: 16 }}>
              {PROVIDERS.map((p) => (
                <label key={p.id} className={"provider-row" + (active === p.id ? " selected" : "")}>
                  <input type="radio" name="provider" checked={active === p.id} onChange={() => setS((cur) => ({ ...cur, provider: p.id }))} />
                  <span style={{ flex: 1 }}>
                    <b style={active === p.id ? { color: "var(--board-deep)" } : undefined}>{p.name}</b>
                    <small>{p.desc}</small>
                  </span>
                  {p.local && <span className="chip chip-pill local-tag">Private · local</span>}
                </label>
              ))}
            </div>

            <h3 style={{ margin: "26px 0 16px" }}>{PROVIDERS.find((p) => p.id === active)?.name} setup</h3>

            {isLocal && (
              <div className="ul-field">
                <span className="field-label">Server address (base URL)</span>
                <input className="ul-input" type="text" value={activeCfg.baseUrl || ""} onChange={(e) => setProviderField(active, "baseUrl", e.target.value)}
                  placeholder={active === "ollama" ? "http://localhost:11434" : "http://localhost:1234"} />
                <span className="field-hint">
                  {active === "ollama"
                    ? <>Where this app should find Ollama. Same computer: the default is right. On another machine, enter that machine&rsquo;s address, e.g. <code>http://192.168.1.50:11434</code> — and set <code>OLLAMA_HOST=0.0.0.0</code> there. Use &ldquo;Test connection&rdquo; to confirm.</>
                    : <>Where this app should find LM Studio. Same computer: the default is right. On another machine: enter its address, e.g. <code>http://192.168.1.50:1234</code> — and in LM Studio&rsquo;s Developer tab enable &ldquo;Serve on Local Network&rdquo;.</>}
                </span>
              </div>
            )}

            {!isLocal && (
              <div className="ul-field">
                <span className="field-label">API key</span>
                <input className="ul-input" type="password" value={activeCfg.apiKey || ""} onChange={(e) => setProviderField(active, "apiKey", e.target.value)} placeholder="Paste your API key" autoComplete="off" />
                <span className="field-hint">Get a key at <a href={KEY_LINKS[active]} target="_blank" rel="noreferrer">{KEY_LINKS[active]}</a>. It is stored only on this computer.</span>
              </div>
            )}

            <div className="ul-field">
              <span className="field-label">Model</span>
              <div style={{ display: "flex", gap: 12, flexWrap: "wrap", alignItems: "flex-end" }}>
                {modelList.length > 0 ? (
                  <select className="ul-input" value={activeCfg.model || ""} onChange={(e) => setProviderField(active, "model", e.target.value)} style={{ flex: 1, minWidth: 220 }}>
                    {!modelList.includes(activeCfg.model) && activeCfg.model && <option value={activeCfg.model}>{activeCfg.model}</option>}
                    {modelList.map((m) => <option key={m} value={m}>{m}</option>)}
                  </select>
                ) : (
                  <input className="ul-input" type="text" value={activeCfg.model || ""} onChange={(e) => setProviderField(active, "model", e.target.value)}
                    placeholder={active === "ollama" ? "e.g. llama3.1:8b" : "Model name"} style={{ flex: 1, minWidth: 220 }} />
                )}
                <button className="btn" onClick={() => refreshModels(active)} disabled={modelsBusy === active}>
                  {modelsBusy === active ? <><span className="spinner" /> Looking…</> : <><IconRefresh size={15} /> Refresh models</>}
                </button>
              </div>
              {modelsErr[active]
                ? <span className="field-hint redpen">{modelsErr[active]}</span>
                : <span className="field-hint">Accuracy tip: bigger models write noticeably better questions. Locally, prefer an 8B+ model; in the cloud, the default models work well.</span>}
            </div>

            <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
              <button className="btn" onClick={() => testConnection(active)} disabled={t?.busy}>
                {t?.busy ? <><span className="spinner" /> Testing…</> : <><IconPlug size={15} /> Test connection</>}
              </button>
              {t && !t.busy && (
                <span className="small" style={{ fontWeight: 600, color: t.ok ? "var(--board)" : "var(--redpen)", display: "inline-flex", alignItems: "center", gap: 4 }}>
                  {t.ok ? <IconCheck size={15} /> : <IconX size={15} />}{t.message}
                </span>
              )}
            </div>
          </section>

          {/* ---- Canvas LMS ---- */}
          <section className="sec" id="canvas">
            <h2>Canvas (LMS) integration</h2>
            <p className="sec-hint">Optional. Lets you push a finished assignment straight into a Canvas course from the editor. You can always skip this and use the downloadable Canvas <code>.zip</code> instead.</p>
            <div className="ul-field">
              <span className="field-label">Canvas web address</span>
              <input className="ul-input" type="text" value={s.canvas?.baseUrl || ""} onChange={(e) => setCanvas("baseUrl", e.target.value)} placeholder="https://yourschool.instructure.com" autoComplete="off" />
              <span className="field-hint">The address you use to log into Canvas — e.g. <code>https://yourschool.instructure.com</code>.</span>
            </div>
            <div className="ul-field">
              <span className="field-label">Access token</span>
              <input className="ul-input" type="password" value={s.canvas?.token || ""} onChange={(e) => setCanvas("token", e.target.value)} placeholder="Paste your Canvas access token" autoComplete="off" />
              <span className="field-hint">In Canvas: <b>Account → Settings → Approved Integrations → + New Access Token</b>. Stored only on this computer. Some schools restrict tokens — if yours does, use the <code>.zip</code> export instead.</span>
            </div>
            <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
              <button className="btn" onClick={testCanvas} disabled={canvasTest?.busy || !s.canvas?.baseUrl || !s.canvas?.token}>
                {canvasTest?.busy ? <><span className="spinner" /> Testing…</> : <><IconPlug size={15} /> Test connection</>}
              </button>
              {canvasTest && !canvasTest.busy && (
                <span className="small" style={{ fontWeight: 600, color: canvasTest.ok ? "var(--board)" : "var(--redpen)", display: "inline-flex", alignItems: "center", gap: 4 }}>
                  {canvasTest.ok ? <IconCheck size={15} /> : <IconX size={15} />}{canvasTest.message}
                </span>
              )}
            </div>
          </section>

          {/* ---- Generation defaults ---- */}
          <section className="sec" id="generation">
            <h2>Generation defaults</h2>
            <label className="check" style={{ marginTop: 14 }}>
              <input type="checkbox" checked={autoOn} onChange={(e) => setGen("auto", e.target.checked)} />
              <span>Set size limits automatically (recommended)<small>Matches source size and response length to the selected model&rsquo;s real context window — local models get safe limits, large cloud models get room for much longer sources and answers.</small></span>
            </label>
            {autoOn && (
              <p className="field-hint" style={{ margin: "0 0 8px 30px", fontStyle: "italic" }}>
                {!activeModel ? "Pick a model above to see its tuned limits."
                  : autoInfo
                    ? `Tuned for ${activeModel}: sources up to ${autoInfo.maxSourceChars.toLocaleString()} characters, responses up to ${autoInfo.maxTokens.toLocaleString()} tokens (context window ≈ ${Math.round(autoInfo.caps.contextTokens / 1000).toLocaleString()}k tokens${autoInfo.caps.source === "fallback" ? ", estimated — couldn't read the model's limits" : ""}).`
                    : <><span className="spinner" /> Checking the model&rsquo;s limits…</>}
              </p>
            )}
            <div className="row" style={{ marginTop: 14 }}>
              <label className="field" style={{ maxWidth: 540 }}>
                <span className="field-label">Temperature — {Number(s.generation.temperature).toFixed(1)}</span>
                <input type="range" min="0" max="1" step="0.1" value={s.generation.temperature} onChange={(e) => setGen("temperature", Number(e.target.value))} style={{ width: "100%" }} />
                <span className="field-hint">Lower = more precise and literal (best for accuracy). 0.2–0.4 recommended.</span>
              </label>
              {!autoOn && (
                <label className="field">
                  <span className="field-label">Max response length (tokens)</span>
                  <input type="number" min="1000" max="64000" step="500" value={s.generation.maxTokens} onChange={(e) => setGen("maxTokens", Math.max(1000, Number(e.target.value) || 8000))} />
                  <span className="field-hint">Raise this if very long assignments come back cut off. Capped to the model&rsquo;s own output limit.</span>
                </label>
              )}
              {!autoOn && (
                <label className="field">
                  <span className="field-label">Max source size (characters)</span>
                  <input type="number" min="4000" max="300000" step="1000" value={s.generation.maxSourceChars} onChange={(e) => setGen("maxSourceChars", Math.max(4000, Number(e.target.value) || 24000))} />
                  <span className="field-hint">Longer sources are trimmed to this before being sent to the model. Local models do better around 16,000–24,000.</span>
                </label>
              )}
            </div>
            <label className="check">
              <input type="checkbox" checked={s.generation.verification !== false} onChange={(e) => setGen("verification", e.target.checked)} />
              <span>Run the accuracy check by default<small>A second pass that verifies every answer against the source. You can still toggle it per assignment.</small></span>
            </label>
          </section>

          <div style={{ display: "flex", justifyContent: "flex-end", paddingTop: 28, borderTop: "1px solid var(--line)", marginTop: 8 }}>
            <button className="btn btn-primary btn-lg" onClick={save} disabled={saving}>
              {saving ? "Saving…" : <><IconCheck size={16} /> Save settings</>}
            </button>
          </div>
        </div>
      </div>

      {toast && <div className="toast">{toast}</div>}
    </div>
  );
}
