// Playground — test any model with a one-off prompt (no session saved).
const { useState, useEffect, useRef, useCallback } = React;

// Normalize HERMES.listModels() (OpenAI-ish {models:[{id}|"id"]}) or window.DATA.models (["id"]).
function pgNormalizeModels(res) {
  const raw = Array.isArray(res) ? res : (res && res.models) || [];
  return raw
    .map((m) => (typeof m === "string" ? m : m && (m.id || m.name)))
    .filter(Boolean);
}

function PagePlayground({ toast }) {
  const [models, setModels] = useState(null); // null = loading
  const [live, setLive] = useState(null);     // null=loading, true=backend, false=offline
  const [model, setModel] = useState("");
  const [system, setSystem] = useState("");
  const [prompt, setPrompt] = useState("");
  const [out, setOut] = useState("");
  const [running, setRunning] = useState(false);
  const [meta, setMeta] = useState(null); // {tokens?, elapsed?}
  const startRef = useRef(0);
  const abortRef = useRef(null);
  const outRef = useRef(null);

  useEffect(() => {
    let alive = true;
    HERMES.listModels()
      .then((res) => {
        if (!alive) return;
        const list = pgNormalizeModels(res);
        setModels(list); setLive(true);
        setModel(list[0] || "");
      })
      .catch(() => {
        if (!alive) return;
        const list = (window.DATA.models || []).slice();
        setModels(list); setLive(false);
        setModel(list[0] || "");
      });
    return () => { alive = false; };
  }, []);

  // auto-scroll output as it streams
  useEffect(() => { if (outRef.current) outRef.current.scrollTop = outRef.current.scrollHeight; }, [out]);

  // abort any in-flight stream on unmount
  useEffect(() => () => { try { abortRef.current && abortRef.current.abort(); } catch (e) { /* */ } }, []);

  const stop = useCallback(() => {
    try { abortRef.current && abortRef.current.abort(); } catch (e) { /* */ }
    abortRef.current = null;
    setRunning(false);
  }, []);

  const run = useCallback(async () => {
    const msg = prompt.trim();
    if (!msg) { toast && toast("Enter a prompt to run."); return; }
    if (live === false) { toast && toast("Connect a backend to run."); return; }

    setOut(""); setMeta(null); setRunning(true);
    startRef.current = Date.now();

    const payload = { message: msg, model, sessionKey: "new" };
    const sys = system.trim();
    if (sys) payload.history = [{ role: "system", content: sys }];

    try {
      const handle = await HERMES.sendStream(payload, {
        chunk: (d) => {
          if (!d) return;
          setOut((prev) => (d.fullReplace ? (d.text || "") : prev + (d.text || "")));
        },
        done: (d) => {
          const elapsed = (Date.now() - startRef.current) / 1000;
          const tokens = d && (d.tokens || d.usage?.total_tokens || d.usage?.totalTokens);
          setMeta({ elapsed, tokens });
          setRunning(false);
          abortRef.current = null;
        },
        error: (d) => {
          toast && toast((d && d.message) || "Stream error");
          setRunning(false);
          abortRef.current = null;
        },
        end: () => { setRunning(false); },
      });
      abortRef.current = handle;
    } catch (err) {
      toast && toast(err.message || "Failed to run");
      setRunning(false);
      abortRef.current = null;
    }
  }, [prompt, system, model, live, toast]);

  const badge = live === false
    ? <span className="badge warn"><span className="d" />preview data</span>
    : live === true
      ? <span className="badge ok"><span className="d" />live</span>
      : null;

  return (
    <div className="page">
      <PageHead eyebrow="Sylar's WorkSpace" title="Playground" sub="Test any model with a one-off prompt — no session saved.">
        {badge}
      </PageHead>

      <div className="card" style={{ display: "grid", gap: 14, maxWidth: 820 }}>
        <div className="split" style={{ alignItems: "end" }}>
          <div className="field">
            <label className="mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em", color: "var(--muted)" }}>Model</label>
            <select className="select" value={model} onChange={(e) => setModel(e.target.value)} disabled={running}>
              {(models || []).map((m) => {
                const mm = modelMeta(m);
                return <option key={m} value={m}>{mm.prov ? `${mm.prov} · ${mm.short}` : m}</option>;
              })}
              {(models && models.length === 0) && <option value="">No models available</option>}
            </select>
          </div>
          <div className="flex" style={{ gap: 8, justifyContent: "flex-end" }}>
            {running
              ? <button className="btn btn-danger" onClick={stop}><Icon name="x" size={16} />Stop</button>
              : <button className="btn btn-primary" onClick={run} disabled={!model}><Icon name="play" size={16} />Run</button>}
          </div>
        </div>

        <div className="field">
          <label className="mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em", color: "var(--muted)" }}>System prompt <span className="faint">(optional)</span></label>
          <input className="input" value={system} onChange={(e) => setSystem(e.target.value)} placeholder="You are a helpful assistant…" disabled={running} />
        </div>

        <div className="field">
          <label className="mono" style={{ fontSize: 11, textTransform: "uppercase", letterSpacing: ".08em", color: "var(--muted)" }}>Prompt</label>
          <textarea
            className="input"
            value={prompt}
            onChange={(e) => setPrompt(e.target.value)}
            placeholder="Ask anything…"
            rows={6}
            style={{ resize: "vertical", minHeight: 130, fontFamily: "inherit", lineHeight: 1.5 }}
            onKeyDown={(e) => { if ((e.metaKey || e.ctrlKey) && e.key === "Enter" && !running) run(); }}
            disabled={running}
          />
          <div className="faint mono" style={{ fontSize: 11, marginTop: 4 }}>⌘/Ctrl + Enter to run</div>
        </div>
      </div>

      <div className="card" style={{ marginTop: "var(--gap)", maxWidth: 820 }}>
        <div className="card-head">
          <h2 className="mono" style={{ fontSize: 12.5, letterSpacing: ".06em", textTransform: "uppercase" }}>Output</h2>
          <div className="flex" style={{ gap: 10, alignItems: "center" }}>
            {running && <span className="badge"><span className="dotpulse" />streaming</span>}
            {meta && (
              <span className="faint mono" style={{ fontSize: 11.5 }}>
                {meta.tokens != null && <span>{fmt(meta.tokens)} tok · </span>}
                {meta.elapsed != null && <span>{meta.elapsed.toFixed(1)}s</span>}
              </span>
            )}
          </div>
        </div>
        {out
          ? <div ref={outRef} className="scroll" style={{ whiteSpace: "pre-wrap", fontSize: 13.5, lineHeight: 1.6, maxHeight: 420, color: "var(--text)" }}>{out}</div>
          : (
            <div className="empty">
              <Icon name="sparkle" size={22} className="faint" />
              <p className="muted" style={{ marginTop: 8 }}>
                {live === false
                  ? "Connect a backend to run a prompt."
                  : "Pick a model, write a prompt, and hit Run to see the reply stream in."}
              </p>
            </div>
          )}
      </div>
    </div>
  );
}

window.PagePlayground = PagePlayground;
