/* playground.jsx - Flow 2: chatbot preview + RAG/quality inspector + deploy CTA */

function SourceRow({ s }) {
  return (
    <div className="row" style={{ gap: 10, padding: "9px 0", borderBottom: "1px solid var(--border)" }}>
      <span style={{ color: "var(--bad)", flex: "none" }}>{I("doc", { style: { width: 16, height: 16 } })}</span>
      <div className="grow" style={{ minWidth: 0 }}>
        <div style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{s.doc}</div>
        {s.page && <div className="caption">{s.page}</div>}
      </div>
      <div className="stack" style={{ alignItems: "flex-end", gap: 3, width: 64, flex: "none" }}>
        <span className="mono" style={{ fontSize: 11, fontWeight: 600, color: s.score >= 0.9 ? "var(--good)" : "var(--ink-2)" }}>{s.score.toFixed(2)}</span>
        <div className="meter" style={{ width: "100%" }}><i style={{ width: (s.score * 100) + "%", background: s.score >= 0.9 ? "var(--good)" : "var(--ink-3)" }} /></div>
      </div>
    </div>
  );
}


function fallbackSuggestedQuestions() {
  return [
    "What are the most important points in the uploaded documents?",
    "What rules, requirements, or limitations should I understand?",
    "What questions should I ask before making a decision?",
  ];
}

function SuggestedQuestionsPanel({ questions, loading, hasApiKey, onRefresh, onAsk, embedded = false }) {
  return (
    <div className={embedded ? "stack" : "card card-pad"} style={embedded ? { gap: 10, padding: "12px 16px 0", borderTop: "1px solid var(--border)", background: "var(--surface)" } : undefined}>
      <div className="between" style={{ gap: 12 }}>
        <div className="stack" style={{ gap: 3 }}>
          <span className={embedded ? "label" : "h3"}>Suggested questions</span>
          <span className="caption">Start with one of these, or refresh for new ideas.</span>
        </div>
        <button className="btn btn-secondary btn-sm" type="button" onClick={onRefresh} disabled={loading} title="Refresh suggested questions">
          {I("refresh", { style: { width: 14, height: 14 } })} {loading ? "Refreshing..." : "Refresh"}
        </button>
      </div>
      <div className="stack" style={{ gap: 7 }}>
        {(questions || []).slice(0, 3).map((question, index) => (
          <button
            key={`${question}-${index}`}
            type="button"
            className="btn btn-secondary"
            onClick={() => onAsk(question)}
            disabled={!hasApiKey || loading}
            style={{ justifyContent: "flex-start", textAlign: "left", whiteSpace: "normal", lineHeight: 1.35, minHeight: embedded ? 36 : 42, padding: embedded ? "8px 11px" : undefined }}
          >
            <span className="badge badge-neutral" style={{ flex: "none" }}>{index + 1}</span>
            <span>{question}</span>
          </button>
        ))}
      </div>
    </div>
  );
}
function AnalysisDetailsDialog({ analysis, onClose }) {
  if (!analysis) return null;
  const unsupported = Array.isArray(analysis.unsupportedClaims) ? analysis.unsupportedClaims : [];
  const supported = Array.isArray(analysis.supportedClaims) ? analysis.supportedClaims : [];
  return (
    <div className="scrim" role="presentation" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" role="dialog" aria-modal="true" aria-labelledby="analysis-detail-title" style={{ maxWidth: 560 }}>
        <div className="card-hd between" style={{ gap: 16 }}>
          <div className="row" style={{ gap: 12, minWidth: 0 }}>
            <div style={{ width: 38, height: 38, borderRadius: "var(--r-sm)", background: "var(--primary-soft)", color: "var(--ink)", display: "flex", alignItems: "center", justifyContent: "center", flex: "none" }}>
              {I("spark", { style: { width: 19, height: 19 } })}
            </div>
            <div>
              <div id="analysis-detail-title" className="h3">Why these scores?</div>
              <div className="caption">Based on the latest answer and retrieved knowledge.</div>
            </div>
          </div>
          <button className="icon-btn" onClick={onClose} style={{ width: 32, height: 32 }}>{I("x", { style: { width: 15, height: 15 } })}</button>
        </div>
        <div className="card-pad stack" style={{ gap: 14 }}>
          <div className="row" style={{ justifyContent: "space-around", gap: 10 }}>
            <div className="stack" style={{ alignItems: "center", gap: 6 }}><Ring value={analysis.quality || 0} tone={toneFor(analysis.quality || 0)} size={58} /><span className="caption" style={{ fontWeight: 700 }}>Quality</span></div>
            <div className="stack" style={{ alignItems: "center", gap: 6 }}><Ring value={analysis.halluc || 0} tone={(analysis.halluc || 0) <= 15 ? "good" : (analysis.halluc || 0) <= 35 ? "warn" : "bad"} size={58} /><span className="caption" style={{ fontWeight: 700 }}>Halluc. risk</span></div>
            <div className="stack" style={{ alignItems: "center", gap: 6 }}><Ring value={analysis.voice || 0} tone={toneFor(analysis.voice || 0)} size={58} /><span className="caption" style={{ fontWeight: 700 }}>Brand voice</span></div>
          </div>
          <div style={{ fontSize: 13, lineHeight: 1.55, color: "var(--ink-2)", padding: 12, border: "1px solid var(--border)", borderRadius: "var(--r-sm)", background: "var(--surface-2)" }}>
            {analysis.reason || "The answer was evaluated against usefulness, source grounding, and the saved assistant instructions."}
          </div>
          {unsupported.length > 0 && (
            <div className="stack" style={{ gap: 8 }}>
              <div className="label">Unsupported or risky claims</div>
              {unsupported.map((claim, index) => <div key={index} className="row" style={{ gap: 8, alignItems: "flex-start", fontSize: 12.5, color: "var(--ink-2)" }}>{I("alert", { style: { width: 14, height: 14, color: "var(--bad)", flex: "none", marginTop: 2 } })}<span>{claim}</span></div>)}
            </div>
          )}
          {supported.length > 0 && (
            <div className="stack" style={{ gap: 8 }}>
              <div className="label">Well-grounded claims</div>
              {supported.map((claim, index) => <div key={index} className="row" style={{ gap: 8, alignItems: "flex-start", fontSize: 12.5, color: "var(--ink-2)" }}>{I("checkCircle", { style: { width: 14, height: 14, color: "var(--good)", flex: "none", marginTop: 2 } })}<span>{claim}</span></div>)}
            </div>
          )}
        </div>
        <div className="row" style={{ justifyContent: "flex-end", gap: 10, padding: "14px 18px", borderTop: "1px solid var(--border)", background: "var(--surface-2)" }}>
          <button className="btn btn-primary" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

const PLAYGROUND_ASSISTANT_PRESETS = {
  sales: {
    label: "Sales assistant",
    openingMessage: "Hi, I am your product assistant. I can help answer questions from the uploaded knowledge base, explain product details, compare options, address common concerns, and suggest the best next step.",
    systemPrompt: `You are a polished, commercially-minded product assistant for the business represented by the uploaded knowledge base.

Your job is to help prospects and customers understand the product, compare options, answer objections, and move toward a confident next step.

Rules:
- Use the uploaded knowledge base as the source of truth.
- Sound professional, warm, specific, and useful. Avoid generic chatbot language.
- When answering sales or evaluation questions, address the user's concern directly, then support the answer with concrete details from the documents.
- If the documents do not contain enough information, say that clearly and suggest what information would be needed.
- Do not invent prices, capabilities, policies, guarantees, timelines, or technical claims that are not supported by the uploaded knowledge base.
- Keep answers concise unless the user asks for detail.`,
  },
  support: {
    label: "Support specialist",
    openingMessage: "Hi, I am your support assistant. I can answer questions from the uploaded knowledge base, clarify policies, explain steps, and help troubleshoot common issues.",
    systemPrompt: `You are a calm, precise support assistant for the business represented by the uploaded knowledge base.

Use the uploaded documents as the source of truth. Help users understand policies, product details, steps, requirements, and troubleshooting guidance. Be concise and practical. If the answer is not supported by the documents, say so clearly and ask for the missing detail. Do not invent policies, prices, timelines, or guarantees.`,
  },
  advisor: {
    label: "Product advisor",
    openingMessage: "Hi, I am your product advisor. I can help explain what this product does, who it is for, how it compares, and which details matter for a buying decision.",
    systemPrompt: `You are a thoughtful product advisor for the business represented by the uploaded knowledge base.

Help users evaluate fit, understand tradeoffs, compare options, and identify the most relevant product details. Ground every claim in the uploaded documents. Answer objections directly and professionally. If the documents do not contain enough support, say that clearly instead of guessing.`,
  },
};

function AssistantSetupPanel({ setup, setSetup, settings, setSettings, activeAgent, user, hasApiKey, ctx, setStatus }) {
  const [saving, setSaving] = React.useState(false);
  const [optimizing, setOptimizing] = React.useState(null);
  const [openingInstruction, setOpeningInstruction] = React.useState("Make it warmer, more specific, and customer-facing.");
  const [systemInstruction, setSystemInstruction] = React.useState("Make it more credible, sales-ready, grounded in the knowledge base, and strict about not inventing claims.");

  const update = (patch) => {
    const next = { ...setup, ...patch };
    setSetup(next);
  };

  const applyPreset = (id) => {
    const preset = PLAYGROUND_ASSISTANT_PRESETS[id];
    if (!preset) return;
    update({ voicePreset: id, openingMessage: preset.openingMessage, systemPrompt: preset.systemPrompt });
  };

  const save = async () => {
    const clean = {
      voicePreset: setup.voicePreset || "sales",
      openingMessage: String(setup.openingMessage || "").trim() || window.ModelOSIntegrations.defaults.openingMessage,
      systemPrompt: String(setup.systemPrompt || "").trim() || window.ModelOSIntegrations.defaults.systemPrompt,
      setupCustomized: true,
    };
    update(clean);
    if (!activeAgent?.modelId) {
      setStatus({ type: "info", text: "Assistant setup saved for the next bot." });
      return;
    }
    setSaving(true);
    try {
      const updated = await window.ModelOSIntegrations.updateAgent(activeAgent.modelId, clean, user);
      const nextAgent = { ...activeAgent, ...updated, modelId: activeAgent.modelId };
      ctx.setActiveAgent(nextAgent);
      window.ModelOSUserStorage.save(user.uid, "activeAgent", nextAgent);
      setSettings(window.ModelOSIntegrations.save({ activeAgent: nextAgent, assistantVoice: clean.voicePreset }));
      setStatus({ type: "success", text: "Assistant setup saved. Clear the chat to preview the new opening message." });
    } catch (err) {
      setStatus({ type: "error", text: err.message || "Failed to save assistant setup." });
    } finally {
      setSaving(false);
    }
  };

  const optimizeField = async (target) => {
    if (!hasApiKey) {
      setStatus({ type: "error", text: "Add your API key before optimizing assistant setup." });
      return;
    }
    const instruction = target === "openingMessage" ? openingInstruction : systemInstruction;
    setOptimizing(target);
    try {
      const improved = await window.ModelOSIntegrations.optimizeAssistantSetup({
        openingMessage: setup.openingMessage,
        systemPrompt: setup.systemPrompt,
        instruction,
        target,
        agentName: activeAgent?.name || "Assistant",
        authUser: user,
      });
      const patch = {};
      if (target === "openingMessage" && improved.openingMessage) patch.openingMessage = improved.openingMessage;
      if (target === "systemPrompt" && improved.systemPrompt) patch.systemPrompt = improved.systemPrompt;
      update(patch);
      setStatus({ type: "success", text: `${target === "openingMessage" ? "Opening message" : "System prompt"} draft improved. Review it, then save.` });
    } catch (err) {
      setStatus({ type: "error", text: err.message || "Failed to optimize assistant setup." });
    } finally {
      setOptimizing(null);
    }
  };

  return (
    <div className="card card-pad">
      <div className="stack" style={{ gap: 4, marginBottom: 14 }}>
        <span className="h3">Assistant setup</span>
        <span className="caption">Tune the first message and behavior, then preview it here.</span>
      </div>
      <div className="row wrap" style={{ gap: 7, marginBottom: 14 }}>
        {Object.entries(PLAYGROUND_ASSISTANT_PRESETS).map(([id, preset]) => (
          <button key={id} type="button" className={cx("btn btn-sm", setup.voicePreset === id ? "btn-primary" : "btn-secondary")} onClick={() => applyPreset(id)}>
            {preset.label}
          </button>
        ))}
      </div>

      <label>
        <span className="field-label">Opening message</span>
        <textarea className="textarea" style={{ minHeight: 84 }} value={setup.openingMessage} onChange={(e) => update({ openingMessage: e.target.value })} />
      </label>
      <div className="stack" style={{ gap: 8, marginTop: 10, padding: 12, border: "1px solid var(--border)", borderRadius: "var(--r-md)", background: "var(--surface-2)" }}>
        <div className="between" style={{ gap: 10 }}>
          <span className="label">Improve opening message</span>
          <span className={cx("badge", hasApiKey ? "badge-good" : "badge-warn")}>{hasApiKey ? "Ready" : "API key required"}</span>
        </div>
        <textarea className="textarea" style={{ minHeight: 68, background: "var(--surface)" }} value={openingInstruction} onChange={(e) => setOpeningInstruction(e.target.value)} placeholder="Tell your model what kind of first message you want." />
        <button className="btn btn-secondary btn-sm" disabled={!hasApiKey || Boolean(optimizing)} onClick={() => optimizeField("openingMessage")}>{optimizing === "openingMessage" ? "Improving..." : "Improve opening message"}</button>
      </div>

      <label style={{ display: "block", marginTop: 12 }}>
        <span className="field-label">System prompt</span>
        <textarea className="textarea mono" style={{ minHeight: 150, fontSize: 12 }} value={setup.systemPrompt} onChange={(e) => update({ systemPrompt: e.target.value })} />
      </label>
      <div className="stack" style={{ gap: 8, marginTop: 10, padding: 12, border: "1px solid var(--border)", borderRadius: "var(--r-md)", background: "var(--surface-2)" }}>
        <div className="between" style={{ gap: 10 }}>
          <span className="label">Improve system prompt</span>
          <span className={cx("badge", hasApiKey ? "badge-good" : "badge-warn")}>{hasApiKey ? "Ready" : "API key required"}</span>
        </div>
        <textarea className="textarea" style={{ minHeight: 78, background: "var(--surface)" }} value={systemInstruction} onChange={(e) => setSystemInstruction(e.target.value)} placeholder="Tell your model how the assistant should behave, sell, support, and stay grounded." />
        <button className="btn btn-secondary btn-sm" disabled={!hasApiKey || Boolean(optimizing)} onClick={() => optimizeField("systemPrompt")}>{optimizing === "systemPrompt" ? "Improving..." : "Improve system prompt"}</button>
      </div>

      <button className="btn btn-primary btn-sm" style={{ marginTop: 12 }} disabled={saving} onClick={save}>{saving ? "Saving..." : "Save setup"}</button>
    </div>
  );
}
function IntegrationSettings({ settings, setSettings, status, activeAgent, user }) {
  const [open, setOpen] = React.useState(false);
  const update = (patch) => {
    const next = window.ModelOSIntegrations.save({ ...settings, ...patch });
    setSettings(next);
  };

  const agent = activeAgent || settings.activeAgent;
  return (
    <div className="card card-pad">
      <div className="between" style={{ gap: 12 }}>
        <div className="stack" style={{ gap: 6, minWidth: 0 }}>
          <span className="eyebrow">RAG Bot</span>
          <div className="row" style={{ gap: 7, minWidth: 0 }}>
            <span className={cx("badge", agent?.modelId ? "badge-good" : "badge-warn")}>{agent?.modelId ? "Connected" : "No PDF"}</span>
            <span className="caption" style={{ whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
              {agent?.name || "Upload a PDF in Knowledge base first"}
            </span>
          </div>
        </div>
        <button className="icon-btn" title={open ? "Collapse settings" : "Expand settings"} onClick={() => setOpen(v => !v)} style={{ width: 34, height: 34, flex: "none" }}>
          {I(open ? "chevD" : "settings", { style: { width: 16, height: 16 } })}
        </button>
      </div>

      <div className={cx("badge", status?.type === "error" ? "badge-bad" : agent?.modelId ? "badge-info" : "badge-neutral")} style={{ alignSelf: "flex-start", marginTop: 12 }}>
        {I(status?.type === "error" ? "alert" : agent?.modelId ? "db" : "info", { style: { width: 13, height: 13 } })}
        {status?.text || (agent?.modelId ? "Using uploaded PDF knowledge" : "Knowledge base required")}
      </div>

      {open && <div className="stack" style={{ gap: 10, marginTop: 14 }}>
        <window.ModelOSLlmApiKeyPanel settings={settings} setSettings={setSettings} activeAgent={activeAgent} authUser={user} compact />
      </div>}
    </div>
  );
}

function PlaygroundView({ ctx }) {
  const { user, loading } = useFirebaseAuth();
  const [msgs, setMsgs] = React.useState([]);
  const [restoredChatKey, setRestoredChatKey] = React.useState(null);
  const [input, setInput] = React.useState("");
  const [typing, setTyping] = React.useState(false);
  const [settings, setSettings] = React.useState(() => {
    const loaded = window.ModelOSIntegrations.load();
    return loaded.mode === "i3" ? loaded : window.ModelOSIntegrations.save({ mode: "i3" });
  });
  const [status, setStatus] = React.useState(null);
  const [knowledgeStatus, setKnowledgeStatus] = React.useState(null);
  const [suggestedQuestions, setSuggestedQuestions] = React.useState(() => fallbackSuggestedQuestions(ctx.activeAgent?.name || "Assistant"));
  const [suggestingQuestions, setSuggestingQuestions] = React.useState(false);
  const [showAnalysisDetails, setShowAnalysisDetails] = React.useState(false);
  const [assistantSetup, setAssistantSetup] = React.useState(() => {
    const loaded = window.ModelOSIntegrations.load();
    const agent = ctx.activeAgent || loaded.activeAgent || {};
    const useCustomSetup = agent.setupCustomized === true;
    return {
      voicePreset: agent.voicePreset || "sales",
      openingMessage: useCustomSetup ? (agent.openingMessage || window.ModelOSIntegrations.defaults.openingMessage) : window.ModelOSIntegrations.defaults.openingMessage,
      systemPrompt: useCustomSetup ? (agent.systemPrompt || window.ModelOSIntegrations.defaults.systemPrompt) : window.ModelOSIntegrations.defaults.systemPrompt,
    };
  });
  const scrollRef = React.useRef(null);
  const activeAgent = ctx.activeAgent || settings.activeAgent;
  const activeModelId = activeAgent?.modelId || "";
  const chatStorageKey = activeModelId ? `playgroundMsgs:${activeModelId}` : "playgroundMsgs:none";
  const isAuthenticated = Boolean(user);
  const hasApiKey = window.ModelOSIntegrations.hasLlmApiKey(settings);
  const openingMessage = assistantSetup.openingMessage || activeAgent?.openingMessage || window.ModelOSIntegrations.defaults.openingMessage;
  const lastBot = [...msgs].reverse().find(m => m.role === "bot");

  React.useEffect(() => {
    const agent = activeAgent || {};
    const useCustomSetup = agent.setupCustomized === true;
    setAssistantSetup({
      voicePreset: agent.voicePreset || "sales",
      openingMessage: useCustomSetup ? (agent.openingMessage || window.ModelOSIntegrations.defaults.openingMessage) : window.ModelOSIntegrations.defaults.openingMessage,
      systemPrompt: useCustomSetup ? (agent.systemPrompt || window.ModelOSIntegrations.defaults.systemPrompt) : window.ModelOSIntegrations.defaults.systemPrompt,
    });
    setStatus(null);
    setShowAnalysisDetails(false);
  }, [activeModelId]);

  React.useEffect(() => {
    if (loading || !isAuthenticated || !user?.uid) return;
    setInput("");
    setShowAnalysisDetails(false);
    if (!activeModelId) {
      setMsgs([]);
      setRestoredChatKey(chatStorageKey);
      return;
    }
    const saved = window.ModelOSUserStorage.load(user.uid, chatStorageKey, []);
    setMsgs(Array.isArray(saved) ? saved : []);
    setRestoredChatKey(chatStorageKey);
  }, [loading, isAuthenticated, user?.uid, activeModelId, chatStorageKey]);

  React.useEffect(() => {
    if (loading || !isAuthenticated || !user?.uid || restoredChatKey !== chatStorageKey || !activeModelId) return;
    window.ModelOSUserStorage.save(user.uid, chatStorageKey, msgs);
  }, [msgs, loading, isAuthenticated, user?.uid, restoredChatKey, chatStorageKey, activeModelId]);

  React.useEffect(() => {
    let cancelled = false;
    if (loading) return;
    if (!isAuthenticated || !activeAgent?.modelId) {
      setKnowledgeStatus(null);
      return;
    }
    window.ModelOSIntegrations.loadKnowledgeStatus(activeAgent.modelId, user)
      .then(data => { if (!cancelled) setKnowledgeStatus(data); })
      .catch(() => { if (!cancelled) setKnowledgeStatus(null); });
    return () => { cancelled = true; };
  }, [isAuthenticated, loading, activeAgent?.modelId]);

  const refreshSuggestedQuestions = async () => {
    const fallback = fallbackSuggestedQuestions(activeAgent?.name || "Assistant");
    if (!hasApiKey || !isAuthenticated || !activeAgent?.modelId) {
      setSuggestedQuestions(fallback);
      return;
    }
    setSuggestingQuestions(true);
    try {
      const next = await window.ModelOSIntegrations.generateSuggestedQuestions({
        modelId: activeAgent.modelId,
        agentName: activeAgent.name || "Assistant",
        authUser: user,
      });
      setSuggestedQuestions(next.length === 3 ? next : fallback);
      setStatus(null);
    } catch (err) {
      setSuggestedQuestions(fallback);
      setStatus({ type: "error", text: err.message || "Failed to refresh suggested questions." });
    } finally {
      setSuggestingQuestions(false);
    }
  };

  React.useEffect(() => {
    setSuggestedQuestions(fallbackSuggestedQuestions(activeAgent?.name || "Assistant"));
  }, [activeAgent?.modelId]);
  const send = async (text) => {
    const q = (text ?? input).trim();
    if (!q || typing) return;
    if (!hasApiKey) {
      setStatus({ type: "error", text: "Add your OpenAI, Claude, Gemini, or GLM API key before using RAG." });
      return;
    }
    const userMsg = { role: "user", text: q };
    const history = [...msgs, userMsg];
    setMsgs(history);
    setInput("");
    setTyping(true);
    const llm = window.ModelOSIntegrations.normalizeLlmSettings(settings);
    setStatus({ type: "info", text: `Calling uploaded-PDF RAG bot with ${llm.provider}...` });

    try {
      let a = await window.ModelOSIntegrations.callRagChat({
        question: q,
        history,
        model: ctx.model,
        tenantId: user.uid,
        authUser: user,
      });
      if (!a) throw new Error("The RAG chatbot did not return an answer.");
      const messageId = a.conversationId || `conv_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
      a = { ...a, id: messageId, conversationId: messageId, analysisStatus: "pending", analysis: null };
      setMsgs(m => [...m, a]);

      const saved = await window.ModelOSIntegrations.persistChatLog({
        question: q,
        answer: a.text,
        sources: a.sources,
        metrics: { quality: 0, halluc: 0, voice: 0, pending: true },
        model: ctx.model,
        modelId: activeAgent?.modelId,
        tenantId: user.uid,
        authUser: user,
        conversationId: messageId,
        mode: settings.mode,
      });
      setStatus({ type: "success", text: saved.persisted ? "Answer saved. Analysis is running." : "Answer ready. Analysis is running." });

      window.ModelOSIntegrations.evaluateRagAnswer({
        question: q,
        answer: a.text,
        sources: a.sources,
        conversationId: messageId,
        model: ctx.model,
        modelId: activeAgent?.modelId,
        authUser: user,
      }).then(async (analysis) => {
        setMsgs(current => current.map(msg => msg.id === messageId ? {
          ...msg,
          quality: analysis.quality,
          halluc: analysis.halluc,
          voice: analysis.voice,
          analysis,
          analysisStatus: "ready",
        } : msg));
        await window.ModelOSIntegrations.persistChatLog({
          question: q,
          answer: a.text,
          sources: a.sources,
          metrics: { quality: analysis.quality, halluc: analysis.halluc, voice: analysis.voice },
          evaluation: analysis,
          model: ctx.model,
          modelId: activeAgent?.modelId,
          tenantId: user.uid,
          authUser: user,
          conversationId: messageId,
          mode: settings.mode,
        }).catch(() => null);
        setStatus({ type: "success", text: "Answer analyzed and saved" });
      }).catch((err) => {
        setMsgs(current => current.map(msg => msg.id === messageId ? { ...msg, analysisStatus: "pending", analysisError: err?.message || "Analysis is taking longer than usual." } : msg));
        setStatus({ type: "info", text: "Answer saved. Analysis is taking longer than usual." });
      });
    } catch (err) {
      const text = err?.message || "Chat request failed.";
      setMsgs(m => [...m, {
        role: "bot",
        text: `I could not reach the RAG service. ${text}`,
        sources: [],
        quality: 0,
        halluc: 100,
        voice: 0,
        error: true,
      }]);
      setStatus({ type: "error", text });
    } finally {
      setTyping(false);
    }
  };

  return (
    <div className="page page-wide">
      <PageHead eyebrow="Step 2 of 3 - Test"
        title="Playground"
        sub="Try your chatbot the way a customer would. Every answer is checked for accuracy, hallucination risk, and brand voice in real time.">
        <button className="btn btn-secondary" onClick={() => { setMsgs([]); if (user?.uid) window.ModelOSUserStorage.remove(user.uid, chatStorageKey); }}>{I("refresh")} Clear</button>
        <button className="btn btn-primary" onClick={() => ctx.setView("deploy")}>{I("rocket")} Deploy to your website</button>
      </PageHead>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 380px", gap: 20, alignItems: "start" }} className="pg-grid">
        {/* chat */}
        <div className="stack fu fu-1" style={{ gap: 16 }}>
        <div className="card" style={{ overflow: "hidden", display: "flex", flexDirection: "column", height: "calc(100vh - 220px)", minHeight: 460 }}>
          <div className="row" style={{ gap: 11, padding: "13px 18px", borderBottom: "1px solid var(--border)", background: "var(--primary)", color: "var(--primary-ink)" }}>
            <div className="avatar" style={{ background: "rgba(255,255,255,.16)", color: "var(--primary-ink)", width: 32, height: 32 }}>{I("bot", { style: { width: 18, height: 18 } })}</div>
            <div className="grow">
              <div style={{ fontSize: 13.5, fontWeight: 700 }}>{activeAgent?.name || "Assistant"}</div>
              <div style={{ fontSize: 11.5, opacity: .7 }}>Powered by Model OS</div>
            </div>
            <span className="badge" style={{ background: "rgba(255,255,255,.16)", color: "var(--primary-ink)" }}><span className="dot" style={{ background: "#46d98a" }} /> Online</span>
          </div>

          <div ref={scrollRef} className="scroll-y" style={{ flex: 1, padding: 18, display: "flex", flexDirection: "column", gap: 12, background: "var(--bg)" }}>
            {msgs.length === 0 && !typing && (
              <div className="bubble bot fu" style={{ maxWidth: 560 }}>
                {openingMessage}
              </div>
            )}
            {msgs.map((m, i) => (
              <div key={i} className={cx("bubble fu", m.role)} style={{ animationDelay: "0s" }}>{m.text}</div>
            ))}
            {typing && (
              <div className="bubble bot" style={{ display: "flex", gap: 4, padding: "13px 16px" }}>
                {[0,1,2].map(d => <span key={d} className="pulse" style={{ width: 7, height: 7, borderRadius: "50%", background: "var(--ink-3)", animationDelay: `${d*0.2}s` }} />)}
              </div>
            )}
          </div>

          <SuggestedQuestionsPanel
            questions={suggestedQuestions}
            loading={suggestingQuestions}
            hasApiKey={hasApiKey}
            onRefresh={refreshSuggestedQuestions}
            onAsk={send}
            embedded
          />

          <div className="row" style={{ gap: 9, padding: 16 }}>
            <input className="input" placeholder={hasApiKey ? "Ask about your knowledge base..." : "Add your API key before asking RAG questions"} value={input}
                   disabled={!hasApiKey}
                   onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} />
            <button className="btn btn-primary" style={{ width: 44, padding: 0 }} disabled={!hasApiKey} onClick={() => send()}>{I("send")}</button>
          </div>
        </div>


          <AssistantSetupPanel
            setup={assistantSetup}
            setSetup={setAssistantSetup}
            settings={settings}
            setSettings={setSettings}
            activeAgent={activeAgent}
            user={user}
            hasApiKey={hasApiKey}
            ctx={ctx}
            setStatus={setStatus}
          />
        </div>

        {/* inspector */}
        <div className="stack fu fu-2" style={{ gap: 16 }}>
          <IntegrationSettings settings={settings} setSettings={setSettings} status={status} activeAgent={ctx.activeAgent} user={user} />

          <div className="card card-pad">
            <div className="between" style={{ gap: 12, marginBottom: 12 }}>
              <div className="eyebrow">Answer analysis</div>
              {lastBot?.analysisStatus === "ready" && <button className="btn btn-secondary btn-sm" onClick={() => setShowAnalysisDetails(true)}>Why these scores?</button>}
            </div>
            <div className="row" style={{ justifyContent: "space-around", gap: 8, marginBottom: 4 }}>
              <div className="stack" style={{ alignItems: "center", gap: 7 }}>
                <Ring value={lastBot?.quality ?? 0} tone={toneFor(lastBot?.quality ?? 0)} />
                <span className="caption" style={{ fontWeight: 600 }}>Quality</span>
              </div>
              <div className="stack" style={{ alignItems: "center", gap: 7 }}>
                <Ring value={lastBot?.halluc ?? 0} tone={(lastBot?.halluc ?? 0) <= 15 ? "good" : (lastBot?.halluc ?? 0) <= 35 ? "warn" : "bad"} />
                <span className="caption" style={{ fontWeight: 600 }}>Halluc. risk</span>
              </div>
              <div className="stack" style={{ alignItems: "center", gap: 7 }}>
                <Ring value={lastBot?.voice ?? 0} tone={toneFor(lastBot?.voice ?? 0)} />
                <span className="caption" style={{ fontWeight: 600 }}>Brand voice</span>
              </div>
            </div>
            {lastBot?.analysisStatus === "ready" ? (
              <div className="row" style={{ gap: 8, marginTop: 14, padding: "10px 12px", background: "var(--good-bg)", borderRadius: "var(--r-md)", border: "1px solid var(--good-border)" }}>
                <span style={{ color: "var(--good-strong)" }}>{I("checkCircle", { style: { width: 16, height: 16 } })}</span>
                <span style={{ fontSize: 12.5, color: "var(--good-strong)", fontWeight: 500 }}>Analysis completed and saved with this conversation.</span>
              </div>
            ) : lastBot?.analysisStatus === "pending" ? (
              <div className="row" style={{ gap: 8, marginTop: 14, padding: "10px 12px", background: "var(--surface-2)", borderRadius: "var(--r-md)", border: "1px solid var(--border)" }}>
                <span>{I("clock", { style: { width: 16, height: 16 } })}</span>
                <span style={{ fontSize: 12.5, color: "var(--ink-2)", fontWeight: 500 }}>Answer is ready. Analysis is running in the background.</span>
              </div>
            ) : lastBot?.analysisStatus === "error" ? (
              <div className="row" style={{ gap: 8, marginTop: 14, padding: "10px 12px", background: "var(--bad-bg)", borderRadius: "var(--r-md)", border: "1px solid var(--bad-border)" }}>
                <span style={{ color: "var(--bad)" }}>{I("alert", { style: { width: 16, height: 16 } })}</span>
                <span style={{ fontSize: 12.5, color: "var(--bad)", fontWeight: 500 }}>{lastBot.analysisError || "Analysis unavailable."}</span>
              </div>
            ) : lastBot ? (
              <div className="caption" style={{ marginTop: 14 }}>This answer has not been analyzed yet.</div>
            ) : (
              <div className="caption" style={{ marginTop: 14 }}>No answer to analyze yet.</div>
            )}
          </div>

          <div className="card card-pad">
            <div className="between" style={{ marginBottom: 6 }}>
              <span className="eyebrow">Retrieved sources</span>
              <span className="caption" style={{ whiteSpace: "nowrap" }}>RAG - top-{lastBot?.sources?.length ?? 0}</span>
            </div>
            <div className="stack">
              {(lastBot?.sources ?? []).map((s, i) => <SourceRow key={i} s={s} />)}
            </div>
            {!lastBot?.sources?.length && <div className="caption" style={{ padding: "10px 0" }}>No retrieved sources yet.</div>}
            <div className="caption" style={{ marginTop: 10 }}>These passages were pulled from your knowledge base to ground the answer.</div>
          </div>

          <div className="card card-pad">
            <div className="between" style={{ marginBottom: 12 }}>
              <span className="eyebrow">Knowledge base</span>
              <button className="btn btn-ghost btn-sm" onClick={() => ctx.setView("knowledge")}>Manage</button>
            </div>
            <div className="stack" style={{ gap: 10 }}>
              <div className="between"><span className="row" style={{ gap: 8, fontSize: 13, whiteSpace: "nowrap" }}>{I("db", { style: { width: 15, height: 15, color: "var(--ink-3)" } })} Sources</span><b className="tnum">{knowledgeStatus?.files?.length ?? 0}</b></div>
              <div className="between"><span className="row" style={{ gap: 8, fontSize: 13, whiteSpace: "nowrap" }}>{I("layers", { style: { width: 15, height: 15, color: "var(--ink-3)" } })} Indexed chunks</span><b className="tnum">{knowledgeStatus?.chunkCount ?? 0}</b></div>
            </div>
          </div>
        </div>
      </div>
      {showAnalysisDetails && <AnalysisDetailsDialog analysis={lastBot?.analysis} onClose={() => setShowAnalysisDetails(false)} />}
      <style>{`@media(max-width:900px){.pg-grid{grid-template-columns:1fr !important}}`}</style>
    </div>
  );
}
window.PlaygroundView = PlaygroundView;
