// SEIS+ Internals — AI Chat panel (inline or sidebar).

function ChatPanel({ open, onToggle, user, inline, onRunCommand, onYoloRun, instanceId }) {
  const [models,    setModels]    = React.useState([]);
  const [model,     setModel]     = React.useState("claude-sonnet-4.6");
  const [messages,  setMessages]  = React.useState([]);
  const [input,     setInput]     = React.useState("");
  const [loading,   setLoading]   = React.useState(false);
  const [streaming, setStreaming] = React.useState("");
  const [yolo,      setYolo]      = React.useState(false);
  const [yoloMsg,   setYoloMsg]   = React.useState("");
  const [chats,     setChats]     = React.useState([]);
  const [activeId,  setActiveId]  = React.useState(null);
  const bottomRef = React.useRef(null);
  const yoloIter  = React.useRef(0);
  const yoloAbort = React.useRef(false);
  const saveTimer = React.useRef(null);

  // Force-save on unmount/close
  React.useEffect(() => {
    return () => {
      clearTimeout(saveTimer.current);
      if (activeId && messages.length > 0) {
        fetch(`/api/chat/sessions/${activeId}`, {
          method: "PUT", credentials: "include", keepalive: true,
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ messages }),
        }).catch(() => {});
      }
    };
  }, [activeId, messages]);
  const pendingSave = React.useRef(null);

  // Load chats from API on mount / instance change
  React.useEffect(() => {
    if (!open || !instanceId) return;
    fetch(`/api/chat/sessions?instance_id=${encodeURIComponent(instanceId)}`, { credentials: "include" })
      .then(r => r.json())
      .then(list => {
        setChats(list);
        if (list.length > 0) {
          const last = list[list.length - 1];
          setActiveId(last.id);
          fetch(`/api/chat/sessions/${last.id}`, { credentials: "include" })
            .then(r => r.json())
            .then(d => { setMessages(d.messages || []); if (d.model) setModel(d.model); })
            .catch(() => setMessages([]));
        } else {
          setActiveId(null);
          setMessages([]);
        }
      })
      .catch(() => { setChats([]); setMessages([]); });
  }, [open, instanceId]);

  // Auto-save messages to DB (debounced)
  React.useEffect(() => {
    if (!activeId || messages.length === 0) return;
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(async () => {
      try {
        const userMsgs = messages.filter(m => m.role === "user");
        const title = userMsgs.length === 1 ? userMsgs[0].content.slice(0, 40) : "";
        const body = { messages };
        if (title) body.title = title;
        await fetch(`/api/chat/sessions/${activeId}`, {
          method: "PUT", credentials: "include",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(body),
        });
        if (title) setChats(prev => prev.map(c => c.id === activeId ? { ...c, title } : c));
      } catch {}
    }, 1500);
  }, [messages, activeId]);

  // New chat
  const newChat = async () => {
    try {
      const r = await fetch("/api/chat/sessions", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ instanceId, title: `Chat ${chats.length + 1}`, model, messages: [] }),
      });
      const d = await r.json();
      setChats(prev => [...prev, { id: d.id, title: `Chat ${chats.length + 1}`, model, instanceId }]);
      setActiveId(d.id);
      setMessages([]);
      setInput("");
    } catch {}
  };

  // Switch chat — load full messages
  const switchChat = async (id) => {
    if (id === activeId) return;
    try {
      const r = await fetch(`/api/chat/sessions/${id}`, { credentials: "include" });
      if (!r.ok) return;
      const d = await r.json();
      setActiveId(d.id);
      setMessages(d.messages || []);
      if (d.model) setModel(d.model);
      setInput("");
    } catch {}
  };

  React.useEffect(() => {
    if (!open) return;
    fetch("/api/chat/models", { credentials: "include" })
      .then(r => r.json())
      .then(m => Array.isArray(m) && m.length && setModels(m))
      .catch(() => {});
  }, [open]);

  React.useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages, streaming, yoloMsg]);

  // ── Extract bash blocks ──
  const extractBash = (text) => {
    const blocks = [];
    const re = /```(?:bash|sh|shell)\s*\n([\s\S]*?)```/g;
    let m;
    while ((m = re.exec(text)) !== null) {
      blocks.push(m[1].trim());
    }
    return blocks;
  };

  // ── Agentic YOLO loop ──
  const yoloLoop = async (text) => {
    if (!onYoloRun) return;
    const blocks = extractBash(text);
    if (blocks.length === 0 || yoloAbort.current) return;

    yoloIter.current++;
    const iter = yoloIter.current;
    setYoloMsg("");

    const allOutputs = [];
    for (const cmd of blocks) {
      if (yoloAbort.current || yoloIter.current !== iter) return;
      setYoloMsg(`▶▶ YOLO running: ${cmd.slice(0, 60)}${cmd.length > 60 ? "..." : ""}`);
      try {
        const out = await onYoloRun(cmd);
        allOutputs.push(`> ${cmd}\n${out || "(no output)"}`);
      } catch (e) {
        allOutputs.push(`> ${cmd}\nERROR: ${e.message}`);
      }
    }

    setYoloMsg("");
    if (yoloAbort.current || yoloIter.current !== iter || allOutputs.length === 0) return;

    // Send output back as user message
    const context = `Command output (iteration ${Math.floor(iter)}):\n${allOutputs.join("\n\n")}\n\nContinue or suggest next steps.`;
    setMessages(prev => [...prev, { role: "user", content: context }]);
    setLoading(true);
    setStreaming("");

    try {
      const all = [...messages, { role: "user", content: context }].map(m => ({ role: m.role, content: m.content }));
      const r = await fetch("/api/chat", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model, messages: all, stream: true, instanceId }),
      });

      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        setMessages(prev => [...prev, { role: "assistant", content: `Error: ${err.detail || r.status}` }]);
        setLoading(false);
        return;
      }

      const reader = r.body.getReader();
      const decoder = new TextDecoder();
      let full = "";
      let buffer = "";
      let errorMsg = null;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split("\n");
        buffer = lines.pop() || "";
        for (const line of lines) {
          const s = line.trim();
          if (!s || !s.startsWith("data:")) continue;
          const json = s.slice(5).trim();
          if (json === "[DONE]") continue;
          try {
            const parsed = JSON.parse(json);
            const delta = parsed.choices?.[0]?.delta;
            const content = delta?.content;
            if (content != null && content !== "") {
              full += content;
              setStreaming(full);
            }
            if (parsed.error) errorMsg = parsed.error.message || parsed.error;
          } catch {}
        }
      }

      if (errorMsg) {
        setMessages(prev => [...prev, { role: "assistant", content: `Error: ${errorMsg}` }]);
      } else {
        const response = full || "(no response — model may be unavailable)";
        setMessages(prev => [...prev, { role: "assistant", content: response }]);
        // Continue YOLO loop if still enabled (max 3 iterations, 2s delay)
        if (yolo && !yoloAbort.current && full && onYoloRun && iter < 3) {
          await new Promise(r => setTimeout(r, 2000));
          setTimeout(() => yoloLoop(response), 200);
        }
      }
    } catch (e) {
      setMessages(prev => [...prev, { role: "assistant", content: `Network error: ${e.message}` }]);
    }

    setStreaming("");
    setLoading(false);
  };

  // ── Trigger YOLO when new assistant message arrives ──
  React.useEffect(() => {
    if (!yolo || !onYoloRun || loading) return;
    const last = messages[messages.length - 1];
    if (!last || last.role !== "assistant" || last._yoloRan) return;
    // Mark to avoid re-triggering
    messages[messages.length - 1]._yoloRan = true;
    setTimeout(() => yoloLoop(last.content), 400);
  }, [messages, yolo, loading]);

  // ── Toggle YOLO off when panel closes ──
  React.useEffect(() => {
    if (!open) { yoloAbort.current = true; setYolo(false); setYoloMsg(""); }
    else yoloAbort.current = false;
  }, [open]);

  // ── Send message ──
  const send = async () => {
    const text = input.trim();
    if (!text || loading) return;
    setInput("");
    const userMsg = { role: "user", content: text };
    const updated = [...messages, userMsg];
    setMessages(updated);
    setLoading(true);
    setStreaming("");
    yoloIter.current++;

    try {
      const all = updated.map(m => ({ role: m.role, content: m.content }));
      const r = await fetch("/api/chat", {
        method: "POST", credentials: "include",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ model, messages: all, stream: true, instanceId }),
      });

      if (!r.ok) {
        const err = await r.json().catch(() => ({}));
        setMessages(prev => [...prev, { role: "assistant", content: `Error: ${err.detail || r.status}` }]);
        setLoading(false);
        return;
      }

      const reader = r.body.getReader();
      const decoder = new TextDecoder();
      let full = "";
      let buffer = "";
      let errorMsg = null;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split("\n");
        buffer = lines.pop() || "";
        for (const line of lines) {
          const s = line.trim();
          if (!s || !s.startsWith("data:")) continue;
          const json = s.slice(5).trim();
          if (json === "[DONE]") continue;
          try {
            const parsed = JSON.parse(json);
            const delta = parsed.choices?.[0]?.delta;
            const content = delta?.content;
            if (content != null && content !== "") {
              full += content;
              setStreaming(full);
            }
            if (parsed.error) errorMsg = parsed.error.message || parsed.error;
          } catch {}
        }
      }

      if (errorMsg) {
        setMessages(prev => [...prev, { role: "assistant", content: `Error: ${errorMsg}` }]);
      } else {
        const resp = full || "(no response — model may be unavailable)";
        setMessages(prev => [...prev, { role: "assistant", content: resp }]);
      }
    } catch (e) {
      setMessages(prev => [...prev, { role: "assistant", content: `Network error: ${e.message}` }]);
    }

    setStreaming("");
    setLoading(false);
  };

  // ── Export chat ──
  const exportChat = () => {
    const text = messages.map(m => `### ${m.role === "user" ? `@${user?.handle || "you"}` : "ASSISTANT"}\n${m.content}`).join("\n\n---\n\n");
    const blob = new Blob([text], { type: "text/markdown" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.download = `chat-${new Date().toISOString().slice(0,10)}.md`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const handleKey = (e) => {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
  };

  if (!open) return null;

  const renderContent = (text) => {
    const parts = text.split(/(```[\s\S]*?```)/g);
    return parts.map((p, i) => {
      if (p.startsWith("```")) {
        const m = p.match(/^```(\w*)\n?([\s\S]*?)```$/);
        const lang = (m?.[1] || "").toLowerCase();
        const code = m?.[2] || p.replace(/^```\w*\n?/, "").replace(/```$/, "");
        const isBash = lang === "bash" || lang === "sh" || lang === "shell";
        return (
          <div key={i} style={{ position: "relative", margin: "8px 0" }}>
            {isBash && onRunCommand && !yolo && (
              <button
                onClick={() => onRunCommand(code.trim())}
                className="btn solid"
                style={{
                  position: "absolute", top: 4, right: 4, zIndex: 5,
                  padding: "2px 8px", fontSize: 9,
                  background: "var(--ok)", borderColor: "var(--ok)", color: "#000",
                }}
                title="Run in terminal"
              >▶ RUN</button>
            )}
            <pre style={{
              background: "var(--bg-2)", padding: isBash && onRunCommand && !yolo ? "10px 64px 10px 14px" : "10px 14px",
              fontSize: 11, overflowX: "auto",
              whiteSpace: "pre-wrap", wordBreak: "break-word", fontFamily: "var(--mono)",
            }}>{code}</pre>
          </div>
        );
      }
      return <span key={i} style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{renderInline(p)}</span>;
    });
  };

  const renderInline = (text) => {
    const tokens = text.split(/(\*\*.*?\*\*|\*.*?\*|`.*?`)/g);
    return tokens.map((t, i) => {
      if (t.startsWith("**") && t.endsWith("**")) return <b key={i}>{t.slice(2, -2)}</b>;
      if (t.startsWith("*") && t.endsWith("*") && t.length > 2) return <i key={i}>{t.slice(1, -1)}</i>;
      if (t.startsWith("`") && t.endsWith("`")) return <code key={i} style={{ background:"var(--bg-2)", padding:"1px 5px", fontFamily:"var(--mono)", fontSize:"0.92em" }}>{t.slice(1, -1)}</code>;
      return <span key={i} style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>{t}</span>;
    });
  };

  const header = (
    <div style={{
      display: "flex", alignItems: "center", gap: 10,
      padding: "10px 14px", borderBottom: "1px solid var(--ink)",
      fontSize: 10, letterSpacing: "0.12em", textTransform: "uppercase",
      flexShrink: 0, flexWrap: "wrap",
    }}>
      <span className="serif" style={{ color: "var(--accent)", fontSize: 18, fontStyle: "italic", letterSpacing: 0 }}>+</span>
      <span style={{ fontWeight: 600 }}>AI ASSISTANT</span>
      {chats.length > 0 && (
        <select value={activeId || ""} onChange={e => { if (e.target.value === "__new") newChat(); else switchChat(Number(e.target.value)); }}
          style={{
            font: "inherit", fontSize: 9, color: "var(--ink)",
            background: "transparent", border: "1px solid var(--rule-soft)",
            padding: "2px 6px", cursor: "pointer", maxWidth: 120,
          }}>
          {chats.slice().reverse().map(c => (
            <option key={c.id} value={c.id}>{c.title || `Chat ${c.id}`}</option>
          ))}
          <option value="__new">+ NEW CHAT</option>
        </select>
      )}
      {onYoloRun && (
        <label className="row gap-4" style={{ cursor: "pointer", userSelect: "none" }} title="Agentic mode: AI auto-runs commands and reads terminal output">
          <input type="checkbox" checked={yolo} onChange={e => { setYolo(e.target.checked); yoloAbort.current = !e.target.checked; if (!e.target.checked) setYoloMsg(""); }}
            style={{ accentColor: "var(--accent)", cursor: "pointer" }} />
          <span className="micro" style={{ color: yolo ? "var(--ok)" : "var(--muted)" }}>YOLO</span>
        </label>
      )}
      <span style={{ color: "var(--muted)", marginLeft: "auto", display: "flex", alignItems: "center", gap: 8 }}>
        {models.length > 0 && (
          <select value={model} onChange={e => setModel(e.target.value)}
            style={{
              font: "inherit", fontSize: 9, color: "var(--ink)",
              background: "transparent", border: "1px solid var(--rule-soft)",
              padding: "2px 6px", cursor: "pointer",
            }}>
            {models.map(m => <option key={m.id} value={m.id}>{m.name}</option>)}
          </select>
        )}
        <button onClick={exportChat} className="micro" style={{ cursor: "pointer", color: "var(--muted)" }} title="Export chat as markdown">⟱ EXPORT</button>
        <button onClick={onToggle} className="micro" style={{ cursor: "pointer", color: "var(--muted)" }}>✕</button>
      </span>
    </div>
  );

  const messagesArea = (
    <div style={{ flex: 1, overflowY: "auto", padding: "14px" }}>
      {messages.length === 0 && !loading && (
        <div style={{ color: "var(--muted)", fontSize: 11, textAlign: "center", marginTop: 20 }}>
          <div className="serif" style={{ fontSize: 24, fontStyle: "italic", marginBottom: 8, color: "var(--accent)" }}>ask me anything.</div>
          <div>SSH · Linux · bash · GPU workflows</div>
        </div>
      )}
      {messages.map((m, i) => (
        <div key={i} style={{ marginBottom: 14 }}>
          <div className="micro" style={{ color: m.role === "user" ? "var(--accent)" : "var(--muted)", marginBottom: 4 }}>
            {m.role === "user" ? `@${user?.handle || "you"}` : "ASSISTANT"}
          </div>
          <div style={{ lineHeight: 1.55 }}>{renderContent(m.content)}</div>
        </div>
      ))}
      {streaming && (
        <div style={{ marginBottom: 14 }}>
          <div className="micro" style={{ color: "var(--muted)", marginBottom: 4 }}>ASSISTANT</div>
          <div style={{ lineHeight: 1.55 }}>{renderContent(streaming)}<span className="tiny" style={{ animation: "pulse 1s infinite", color: "var(--accent)" }}>█</span></div>
        </div>
      )}
      {yoloMsg && (
        <div style={{ marginBottom: 14 }}>
          <div className="micro" style={{ color: "var(--ok)", marginBottom: 4, animation: "pulse 1.5s infinite" }}>YOLO</div>
          <div style={{ lineHeight: 1.55, color: "var(--muted)", fontStyle: "italic", fontSize: 11 }}>{yoloMsg}</div>
        </div>
      )}
      <div ref={bottomRef} />
    </div>
  );

  const inputArea = (
    <div style={{
      borderTop: "1px solid var(--ink)", padding: "10px 14px",
      display: "flex", gap: 8, flexShrink: 0,
    }}>
      <textarea
        value={input}
        onChange={e => setInput(e.target.value)}
        onKeyDown={handleKey}
        placeholder={loading ? "Waiting..." : yolo ? "YOLO active — AI runs commands automatically..." : "Ask about SSH, Linux, bash..."}
        rows={2}
        disabled={loading}
        style={{
          flex: 1, font: "inherit", fontSize: 12, resize: "none",
          color: "var(--ink)", background: "transparent",
          border: "1px solid var(--rule-soft)", padding: "8px 10px",
          outline: "none",
        }}
        onFocus={e => e.currentTarget.style.borderColor = "var(--ink)"}
        onBlur={e => e.currentTarget.style.borderColor = "var(--rule-soft)"}
      />
      <button
        onClick={send}
        disabled={loading || !input.trim()}
        className="btn solid"
        style={{ padding: "6px 12px", fontSize: 10, alignSelf: "flex-end", opacity: loading || !input.trim() ? 0.4 : 1 }}
      >
        SEND <span className="arr">→</span>
      </button>
    </div>
  );

  if (inline) {
    return (
      <div style={{
        display: "flex", flexDirection: "column", height: "100%",
        borderLeft: "1px solid var(--ink)",
        fontFamily: "var(--mono)", fontSize: 12,
      }}>
        {header}
        {messagesArea}
        {inputArea}
      </div>
    );
  }

  return (
    <div style={{
      position: "fixed", top: 0, right: 0, bottom: 0,
      width: "min(480px, 100vw)", zIndex: 250,
      background: "var(--paper)", borderLeft: "1px solid var(--ink)",
      display: "flex", flexDirection: "column",
      fontFamily: "var(--mono)", fontSize: 12,
    }}>
      {header}
      {messagesArea}
      {inputArea}
    </div>
  );
}

window.SEIS_CHAT = { ChatPanel };
