"use client";
// app/settings/page.jsx — choose and configure your AI provider, all stored locally.
import { useEffect, useRef, useState } from "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",
};

export default function SettingsPage() {
  const [s, setS] = useState(null);
  const [models, setModels] = useState({});      // provider -> string[]
  const [modelsBusy, setModelsBusy] = useState("");
  const [modelsErr, setModelsErr] = useState({}); // provider -> error
  const [test, setTest] = useState({});           // provider -> {busy, ok, message}
  const [canvasTest, setCanvasTest] = useState(null); // {busy, ok, message}
  const [autoInfo, setAutoInfo] = useState(null);  // resolved auto limits for the active model
  const [saving, setSaving] = useState(false);
  const [toast, setToast] = useState("");
  const [error, setError] = useState("");
  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;

  // Preview the auto-tuned limits whenever the model selection (or key) changes.
  // Debounced so typing an API key doesn't fire a request per keystroke.
  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]);

  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) });
    }
  }

  // Downscale the logo client-side (max 512px) so it stays a small data URL in db.json
  // while staying crisp at the printed header size.
  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 <p className="muted"><span className="spinner" /> Loading…</p>;

  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>
      <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="card">
        <h2>Teacher &amp; school</h2>
        <p className="field-hint" style={{ marginTop: 4 }}>
          Shown in the header of every printed and exported assignment — leave anything blank to omit it.
        </p>
        <div className="row" style={{ marginTop: 14 }}>
          <label className="field">
            <span className="field-label">Teacher name</span>
            <input type="text" value={s.profile?.teacherName || ""}
              onChange={(e) => setProfile("teacherName", e.target.value)}
              placeholder="e.g. Mr. Drew" />
          </label>
          <label className="field">
            <span className="field-label">Class / course</span>
            <input type="text" value={s.profile?.className || ""}
              onChange={(e) => setProfile("className", e.target.value)}
              placeholder="e.g. 7th Grade Science — Period 3" />
          </label>
          <label className="field">
            <span className="field-label">School name</span>
            <input type="text" value={s.profile?.schoolName || ""}
              onChange={(e) => setProfile("schoolName", e.target.value)}
              placeholder="e.g. Lincoln Middle School" />
          </label>
        </div>
        <div className="field">
          <span className="field-label">School logo or mascot (optional)</span>
          <div style={{ display: "flex", gap: 12, alignItems: "center", flexWrap: "wrap" }}>
            {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" }}>
              {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-danger" 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>
        </div>
      </div>

      <div className="card">
        <h2>AI provider</h2>
        <div style={{ marginTop: 14 }}>
          {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>
                <b>{p.name}</b>
                <small>{p.desc}</small>
              </span>
              {p.local && <span className="chip local-tag">Private · local</span>}
            </label>
          ))}
        </div>

        <hr className="hr" />
        <h3 style={{ marginBottom: 12 }}>{PROVIDERS.find((p) => p.id === active)?.name} setup</h3>

        {isLocal && (
          <label className="field">
            <span className="field-label">Server address (base URL)</span>
            <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. Ollama on another machine (or this app in Docker on a different box): enter that machine&rsquo;s address, e.g. <code>http://192.168.1.50:11434</code> — and on that machine set <code>OLLAMA_HOST=0.0.0.0</code> so Ollama accepts network connections. Use &ldquo;Test connection&rdquo; below to confirm.</>
                : <>Where this app should find LM Studio. Same computer: the default is right. LM Studio 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;. Use &ldquo;Test connection&rdquo; below to confirm.</>}
            </span>
          </label>
        )}

        {!isLocal && (
          <label className="field">
            <span className="field-label">API key</span>
            <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>
          </label>
        )}

        <label className="field">
          <span className="field-label">Model</span>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            {modelList.length > 0 ? (
              <select 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
                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…</> : "Refresh models"}
            </button>
          </div>
          {modelsErr[active] && <span className="field-hint" style={{ color: "var(--redpen)" }}>{modelsErr[active]}</span>}
          {!modelsErr[active] && (
            <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>
          )}
        </label>

        <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…</> : "Test connection"}
          </button>
          {t && !t.busy && (
            <span className={"small " + (t.ok ? "" : "redpen")} style={t.ok ? { color: "var(--board)", fontWeight: 600 } : { fontWeight: 600 }}>
              {t.ok ? "✓ " : "✕ "}{t.message}
            </span>
          )}
        </div>
      </div>

      <div className="card">
        <h2>Canvas (LMS) integration</h2>
        <p className="field-hint" style={{ marginTop: 4 }}>
          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>
        <label className="field" style={{ marginTop: 14 }}>
          <span className="field-label">Canvas web address</span>
          <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>
        </label>
        <label className="field">
          <span className="field-label">Access token</span>
          <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>
        </label>
        <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…</> : "Test connection"}
          </button>
          {canvasTest && !canvasTest.busy && (
            <span className="small" style={canvasTest.ok ? { color: "var(--board)", fontWeight: 600 } : { fontWeight: 600 }}>
              {canvasTest.ok ? "✓ " : "✕ "}{canvasTest.message}
            </span>
          )}
        </div>
      </div>

      <div className="card">
        <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'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 4px 30px" }}>
            {!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's limits…</>}
          </p>
        )}
        <div className="row" style={{ marginTop: 14 }}>
          <label className="field">
            <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%", accentColor: "var(--board)" }}
            />
            <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'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 with small context windows 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>
      </div>

      <div style={{ display: "flex", marginTop: 18 }}>
        <span className="spacer" />
        <button className="btn btn-primary btn-lg" onClick={save} disabled={saving}>
          {saving ? "Saving…" : "Save settings"}
        </button>
      </div>

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