// ============ PERSONAS TAB ============
// Synthetic-customer test bots (up to 10) that chat with a chosen agent
// for a set duration. After each session, a Claude grader scores 1–10
// with feedback so you can see whether the agent is actually improving.

const PERSONAS_SERVER = () =>
  (typeof window !== "undefined" && window.CITRUS_CONFIG && window.CITRUS_CONFIG.SERVER_URL) ||
  "http://localhost:3001";

const PERSONAS_MAX = 10;

const VIBE_SUGGESTIONS = [
  "Curious lead — asks about pricing and availability",
  "Price-shopper who refuses to give specs",
  "Angry complainer about a past order",
  "Vague customer who needs lots of clarifying questions",
  "Off-topic chatter who tries to derail",
  "Technical buyer who already knows the spec",
  "First-time buyer asking the basics",
  "Impatient one-line message sender",
  "Arabic speaker who switches to English",
  "Customer who describes a part to identify",
];

const EVALUATION_FOCUS = {
  mixed: "Contradictions + high-occurrence lessons",
  contradictions: "Contradictions only",
  high_occurrence: "High-occurrence lessons only",
};

const emptyDraft = () => ({
  name: "",
  vibe: "",
  agentId: "",
  durationMin: 5,
  triggerMode: "manual",
  scheduleEveryMin: 60,
  evaluationFocus: "mixed",
});

function PersonasTab({ agents }) {
  const byId = Object.fromEntries(agents.map((a) => [a.id, a]));
  const [list, setList]         = useState([]);
  const [openId, setOpenId]     = useState(null);
  const [adding, setAdding]     = useState(false);
  const [draft, setDraft]       = useState(emptyDraft());
  const [detail, setDetail]     = useState(null); // full record for openId
  const [tick, setTick]         = useState(0);    // forces 1s re-render of timers
  // Teach-the-agent state: which transcript message index has its inline
  // input open (keyed by personaId-msgIdx) and the current draft note.
  const [teachOpen,  setTeachOpen]  = useState(null);
  const [teachNote,  setTeachNote]  = useState("");
  const [teachBusy,  setTeachBusy]  = useState(false);
  const [createBusy, setCreateBusy] = useState(false); // disables Create while POST in flight

  // Promote a grade entry's feedback to a manual learning. Used when a
  // session scored OK but the grader still cited a recurring weakness —
  // bypasses the auto-learn score floor so stubborn issues can be
  // forced into the agent's prompt regardless of overall score.
  const [promoteBusy, setPromoteBusy] = useState(null); // grade id while in flight
  const promoteGrade = (persona, grade) => {
    setPromoteBusy(grade.id);
    const note = [
      grade.feedback,
      grade.comparison?.reason ? `Reference comparison: ${grade.comparison.reason}` : "",
    ].filter(Boolean).join(" ");
    const ctx = [
      `Grade ${grade.score}/10 · axes scope=${grade.axes?.scope}/5 leadCapture=${grade.axes?.leadCapture}/5 toneMatch=${grade.axes?.toneMatch}/5 usefulness=${grade.axes?.usefulness}/5`,
      ...(grade.issues || []).map((i) => `Turn [${i.turnIndex}]: ${i.issue}`),
    ].join("\n");
    fetch(`${PERSONAS_SERVER()}/agents/${persona.agentId}/teach`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ note, context: ctx }),
    })
      .then((r) => r.ok ? r.json() : r.json().then((j) => Promise.reject(j)))
      .then(() => {
        const a = byId[persona.agentId];
        window.toast && window.toast(`${a ? a.name : "Agent"} learned from this grade`, "good");
      })
      .catch((err) => window.toast && window.toast(err.error || "Couldn't promote grade", "warn"))
      .finally(() => setPromoteBusy(null));
  };

  // Submit a teach correction for a specific agent message in the transcript.
  // Surrounding turns (up to 2 before) go in as context so Claude can
  // generalize the lesson without losing what the customer was asking.
  const submitTeach = (persona, msgIdx) => {
    if (!teachNote.trim()) { window.toast && window.toast("Write what was wrong", "warn"); return; }
    setTeachBusy(true);
    const tx = persona.transcript || [];
    const start = Math.max(0, msgIdx - 2);
    const ctx = tx.slice(start, msgIdx + 1)
      .map((m) => `${m.role === "customer" ? "CUSTOMER" : "AGENT"}: ${m.content}`)
      .join("\n");
    fetch(`${PERSONAS_SERVER()}/agents/${persona.agentId}/teach`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ note: teachNote, context: ctx }),
    })
      .then((r) => r.ok ? r.json() : r.json().then((j) => Promise.reject(j)))
      .then((j) => {
        const a = byId[persona.agentId];
        window.toast && window.toast(`${a ? a.name : "Agent"} just learned from that`, "good");
        setTeachOpen(null);
        setTeachNote("");
      })
      .catch((err) => window.toast && window.toast(err.error || "Couldn't save the correction", "warn"))
      .finally(() => setTeachBusy(false));
  };

  // 1s tick so the "X min remaining" countdowns update live without refetching.
  useEffect(() => {
    const t = setInterval(() => setTick((n) => n + 1), 1000);
    return () => clearInterval(t);
  }, []);

  // Initial load
  const reload = () => {
    fetch(`${PERSONAS_SERVER()}/personas`)
      .then((r) => r.ok ? r.json() : [])
      .then(setList)
      .catch(() => {});
  };
  useEffect(reload, []);

  // Live updates via SSE — same /events stream the rest of the dashboard uses.
  useEffect(() => {
    let es;
    try {
      es = new EventSource(`${PERSONAS_SERVER()}/events`);
    } catch { return; }
    const upsert = (e) => {
      try {
        const d = JSON.parse(e.data);
        if (!d.persona) return;
        setList((xs) => {
          const i = xs.findIndex((x) => x.id === d.persona.id);
          const lite = { ...d.persona, transcript: undefined, transcriptLen: (d.persona.transcript || []).length };
          if (i >= 0) { const next = xs.slice(); next[i] = lite; return next; }
          return [...xs, lite];
        });
        if (openId === d.persona.id) setDetail(d.persona);
      } catch {}
    };
    const remove = (e) => {
      try {
        const d = JSON.parse(e.data);
        setList((xs) => xs.filter((x) => x.id !== d.personaId));
        if (openId === d.personaId) { setOpenId(null); setDetail(null); }
      } catch {}
    };
    const messageOrGrade = (e) => {
      try {
        const d = JSON.parse(e.data);
        if (openId && d.personaId === openId) {
          // Refetch full record for the open card
          fetch(`${PERSONAS_SERVER()}/personas/${openId}`)
            .then((r) => r.ok ? r.json() : null)
            .then((p) => p && setDetail(p))
            .catch(() => {});
        }
      } catch {}
    };
    es.addEventListener("persona", upsert);
    es.addEventListener("persona_deleted", remove);
    es.addEventListener("persona_message", messageOrGrade);
    es.addEventListener("persona_grade", messageOrGrade);
    return () => { try { es.close(); } catch {} };
  }, [openId]);

  // Fetch full record when opening a card
  useEffect(() => {
    if (!openId) { setDetail(null); return; }
    fetch(`${PERSONAS_SERVER()}/personas/${openId}`)
      .then((r) => r.ok ? r.json() : null)
      .then((p) => p && setDetail(p))
      .catch(() => {});
  }, [openId]);

  const create = () => {
    if (createBusy) return;                                                            // guard against double-click
    if (!draft.name.trim())                { window.toast && window.toast("Give the persona a name", "warn"); return; }
    if (!draft.agentId)                    { window.toast && window.toast("Pick an agent", "warn"); return; }
    setCreateBusy(true);
    fetch(`${PERSONAS_SERVER()}/personas`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(draft),
    })
      .then((r) => r.ok ? r.json() : r.json().then((j) => Promise.reject(j)))
      .then((p) => {
        // Upsert by id — the SSE "persona" broadcast may have already added
        // it. Without this check we'd render two cards for one create.
        setList((xs) => {
          if (xs.some((x) => x.id === p.id)) return xs;
          return [...xs, { ...p, transcript: undefined, transcriptLen: 0 }];
        });
        setAdding(false);
        setDraft(emptyDraft());
        window.toast && window.toast("Persona created", "good");
      })
      .catch((err) => window.toast && window.toast(err.error || "Couldn't create persona", "warn"))
      .finally(() => setCreateBusy(false));
  };

  const action = (id, path) => {
    fetch(`${PERSONAS_SERVER()}/personas/${id}/${path}`, { method: "POST" })
      .then((r) => r.ok ? r.json() : null)
      .then(reload)
      .catch(() => {});
  };
  const remove = (id) => {
    if (!confirm("Delete this persona and its grade history?")) return;
    fetch(`${PERSONAS_SERVER()}/personas/${id}`, { method: "DELETE" })
      .then(() => { setList((xs) => xs.filter((x) => x.id !== id)); if (openId === id) setOpenId(null); })
      .catch(() => {});
  };

  const remaining = (p) => {
    if (!p.runUntil) return null;
    const ms = new Date(p.runUntil).getTime() - Date.now();
    if (ms <= 0) return "0:00";
    const total = Math.floor(ms / 1000);
    return `${Math.floor(total / 60)}:${String(total % 60).padStart(2, "0")}`;
  };
  const lastGrade = (p) => (p.grades && p.grades.length) ? p.grades[p.grades.length - 1] : null;
  const avgGrade = (p) => {
    if (!p.grades || p.grades.length === 0) return null;
    return Math.round((p.grades.reduce((s, g) => s + g.score, 0) / p.grades.length) * 10) / 10;
  };

  return (
    <div style={{ padding: "24px 32px", maxWidth: 1200, margin: "0 auto" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 24 }}>
        <div>
          <h1 style={{ fontFamily: "Fraunces, serif", fontSize: 32, margin: 0, marginBottom: 4 }}>Personas</h1>
          <div style={{ color: "var(--ds-muted, #888)", fontSize: 14 }}>
            Evaluator personas ({list.length} / {PERSONAS_MAX}). Run on demand, on a schedule, or after each learning to verify the lesson was useful.
          </div>
        </div>
        <button
          className="ds-new"
          disabled={list.length >= PERSONAS_MAX}
          onClick={() => setAdding(true)}
          style={{ opacity: list.length >= PERSONAS_MAX ? 0.5 : 1 }}
        >
          + New persona
        </button>
      </div>

      {/* New-persona form */}
      {adding && (
        <div style={{ background: "var(--ds-card, #fff)", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 12, padding: 20, marginBottom: 24 }}>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginBottom: 16 }}>
            <label style={{ display: "block" }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Name</div>
              <input
                type="text"
                value={draft.name}
                onChange={(e) => setDraft({ ...draft, name: e.target.value })}
                placeholder="Mariam — first-time buyer"
                style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
              />
            </label>
            <label style={{ display: "block" }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Target agent</div>
              <select
                value={draft.agentId}
                onChange={(e) => setDraft({ ...draft, agentId: e.target.value })}
                style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
              >
                <option value="">Pick an agent…</option>
                {agents.map((a) => <option key={a.id} value={a.id}>{a.name}</option>)}
              </select>
            </label>
          </div>
          <label style={{ display: "block", marginBottom: 16 }}>
            <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Vibe (1 line — what kind of customer is this?)</div>
            <input
              type="text"
              value={draft.vibe}
              onChange={(e) => setDraft({ ...draft, vibe: e.target.value })}
              placeholder="Curious lead — asks about pricing and availability"
              style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
            />
            <div style={{ fontSize: 11, color: "var(--ds-muted, #888)", marginTop: 6 }}>
              Suggestions: {VIBE_SUGGESTIONS.slice(0, 4).map((v, i) => (
                <a key={i} onClick={() => setDraft({ ...draft, vibe: v })} style={{ cursor: "pointer", textDecoration: "underline", marginRight: 10 }}>{v.split(" — ")[0]}</a>
              ))}
            </div>
          </label>
          <label style={{ display: "block", marginBottom: 16 }}>
            <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>How long to run (minutes)</div>
            <input
              type="number"
              min={1}
              max={120}
              value={draft.durationMin}
              onChange={(e) => setDraft({ ...draft, durationMin: parseInt(e.target.value, 10) || 5 })}
              style={{ width: 120, padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
            />
          </label>
          <div style={{
            display: "grid",
            gridTemplateColumns: "minmax(180px, 0.8fr) minmax(160px, 0.7fr) minmax(220px, 1fr)",
            gap: 16,
            marginBottom: 16,
          }}>
            <label style={{ display: "block" }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Trigger</div>
              <select
                value={draft.triggerMode}
                onChange={(e) => setDraft({ ...draft, triggerMode: e.target.value })}
                style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
              >
                <option value="manual">Manual only</option>
                <option value="scheduled">Scheduled</option>
                <option value="after_learning">After each learning</option>
              </select>
            </label>
            <label style={{ display: "block", opacity: draft.triggerMode === "scheduled" ? 1 : 0.45 }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Every (minutes)</div>
              <input
                type="number"
                min={5}
                max={1440}
                disabled={draft.triggerMode !== "scheduled"}
                value={draft.scheduleEveryMin}
                onChange={(e) => setDraft({ ...draft, scheduleEveryMin: parseInt(e.target.value, 10) || 60 })}
                style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
              />
            </label>
            <label style={{ display: "block" }}>
              <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 6 }}>Evaluator focus</div>
              <select
                value={draft.evaluationFocus}
                onChange={(e) => setDraft({ ...draft, evaluationFocus: e.target.value })}
                style={{ width: "100%", padding: "8px 10px", border: "1px solid var(--ds-line, #e5e5e5)", borderRadius: 8 }}
              >
                {Object.entries(EVALUATION_FOCUS).map(([id, label]) => <option key={id} value={id}>{label}</option>)}
              </select>
            </label>
          </div>
          <div style={{
            border: "1px solid #3B7CFF2a",
            background: "#3B7CFF0f",
            borderRadius: 10,
            padding: "10px 12px",
            fontSize: 12,
            lineHeight: 1.45,
            color: "var(--ds-muted, #555)",
            marginBottom: 16,
          }}>
            The evaluator uses the vibe as test input. Triggered runs sharpen that vibe with active learnings, dropped or superseded lessons, and repeated lessons, then score the agent at the end of the session.
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button
              onClick={create}
              className="ds-new"
              disabled={createBusy}
              style={{ opacity: createBusy ? 0.5 : 1, cursor: createBusy ? "wait" : "pointer" }}
            >{createBusy ? "Creating…" : "Create"}</button>
            <button onClick={() => { setAdding(false); setDraft(emptyDraft()); }} style={{ background: "transparent", border: "1px solid var(--ds-line, #e5e5e5)", padding: "8px 16px", borderRadius: 8, cursor: "pointer" }}>Cancel</button>
          </div>
        </div>
      )}

      {/* Persona cards */}
      {list.length === 0 && !adding ? (
        <div style={{ textAlign: "center", padding: "60px 20px", color: "var(--ds-muted, #888)" }}>
          <div style={{ fontSize: 48, marginBottom: 12 }}>🎭</div>
          <div style={{ fontSize: 18, fontWeight: 600, marginBottom: 6 }}>No personas yet</div>
          <div style={{ fontSize: 14, marginBottom: 20 }}>Create up to 10 evaluator personas to stress-test lessons and grade the agent.</div>
          <button onClick={() => setAdding(true)} className="ds-new">+ Create your first persona</button>
        </div>
      ) : (
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))", gap: 16 }}>
          {list.map((p) => {
            const a = byId[p.agentId];
            const last = lastGrade(p);
            const avg  = avgGrade(p);
            const isRunning = p.status === "running";
            const isPaused  = p.status === "paused";
            const isGrading = p.status === "grading";
            const rem = remaining(p);
            void tick; // just to depend on tick so re-renders happen
            return (
              <div key={p.id} style={{
                background: "var(--ds-card, #fff)", border: "1px solid var(--ds-line, #e5e5e5)",
                borderRadius: 12, padding: 16, position: "relative",
                outline: isRunning ? "2px solid #2DAF6B" : "none",
              }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "start", marginBottom: 8 }}>
                  <div>
                    <div style={{ fontWeight: 600, fontSize: 15 }}>{p.name}</div>
                    <div style={{ fontSize: 12, color: "var(--ds-muted, #888)" }}>vs. {a ? a.name : "(missing agent)"}</div>
                  </div>
                  <div style={{
                    fontSize: 11, padding: "3px 8px", borderRadius: 99,
                    background: isRunning ? "#2DAF6B22" : isGrading ? "#3B7CFF22" : isPaused ? "#FFCA7D44" : "#0001",
                    color:      isRunning ? "#196C42"   : isGrading ? "#1F4FB5"   : isPaused ? "#7A4400"   : "#666",
                    fontWeight: 600, textTransform: "uppercase", letterSpacing: 0.4,
                  }}>
                    {isRunning ? `▶ running · ${rem || ""}` : isGrading ? "⚖ grading…" : isPaused ? "⏸ paused" : "idle"}
                  </div>
                </div>
                {p.vibe && (
                  <div style={{ fontSize: 13, color: "var(--ds-muted, #555)", fontStyle: "italic", marginBottom: 12, lineHeight: 1.4 }}>
                    "{p.vibe}"
                  </div>
                )}
                <div style={{ display: "flex", gap: 16, fontSize: 12, marginBottom: 12, color: "var(--ds-muted, #777)" }}>
                  <div><b style={{ color: "var(--ds-text, #111)" }}>{p.durationMin}m</b> duration</div>
                  <div><b style={{ color: "var(--ds-text, #111)" }}>{p.transcriptLen || 0}</b> messages</div>
                  <div><b style={{ color: "var(--ds-text, #111)" }}>{(p.grades || []).length}</b> sessions</div>
                </div>
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 12 }}>
                  <span style={chipStyle("#3B7CFF")}>
                    {p.triggerMode === "scheduled"
                      ? `scheduled · ${p.scheduleEveryMin || 60}m`
                      : p.triggerMode === "after_learning"
                        ? "after each learning"
                        : "manual"}
                  </span>
                  <span style={chipStyle("#2DAF6B")}>{EVALUATION_FOCUS[p.evaluationFocus || "mixed"]}</span>
                  {p.triggerReason && p.triggerReason !== "manual" && (
                    <span style={chipStyle("#E85D1A")}>last: {p.triggerReason.replace("_", " ")}</span>
                  )}
                </div>
                {detail && openId === p.id && detail.sessionVibe && detail.sessionVibe !== detail.vibe && (
                  <div style={{ fontSize: 11, color: "var(--ds-muted, #666)", background: "#00000008", borderRadius: 8, padding: "8px 10px", marginBottom: 12, lineHeight: 1.4 }}>
                    <b style={{ color: "var(--ds-text, #111)" }}>Current evaluator input:</b> {detail.sessionVibe.slice(0, 280)}{detail.sessionVibe.length > 280 ? "…" : ""}
                  </div>
                )}
                {(last || avg !== null) && (
                  <div style={{ padding: 10, background: "#0000000a", borderRadius: 8, marginBottom: 12 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                      {last && (
                        <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
                          <div style={{
                            fontSize: 22, fontWeight: 700, fontFamily: "Fraunces, serif",
                            color: last.score >= 7 ? "#2DAF6B" : last.score >= 5 ? "#E85D1A" : "#B4488C",
                          }}>{last.score}<span style={{ fontSize: 12, opacity: 0.6 }}>/10</span></div>
                          <div style={{ fontSize: 10, color: "#888" }}>last</div>
                        </div>
                      )}
                      {avg !== null && (
                        <div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
                          <div style={{ fontSize: 16, fontWeight: 600 }}>{avg}</div>
                          <div style={{ fontSize: 10, color: "#888" }}>avg</div>
                        </div>
                      )}
                      {last && last.feedback && (
                        <div style={{ flex: 1, fontSize: 12, color: "var(--ds-muted, #555)", lineHeight: 1.4 }}>
                          {last.feedback}
                        </div>
                      )}
                    </div>
                    {last && last.axes && (
                      <div style={{ display: "flex", gap: 6, marginTop: 10 }}>
                        {[
                          ["scope", "Scope"],
                          ["leadCapture", "Lead"],
                          ["toneMatch", "Tone"],
                          ["usefulness", "Useful"],
                        ].map(([k, label]) => {
                          const v = last.axes[k] || 0;
                          const color = v >= 4 ? "#2DAF6B" : v >= 3 ? "#E85D1A" : "#B4488C";
                          return (
                            <div key={k} style={{ flex: 1 }}>
                              <div style={{ fontSize: 9, color: "#888", textTransform: "uppercase", letterSpacing: 0.4, marginBottom: 2 }}>{label}</div>
                              <div style={{ display: "flex", gap: 1.5 }}>
                                {[1, 2, 3, 4, 5].map((n) => (
                                  <div key={n} style={{ flex: 1, height: 4, borderRadius: 2, background: n <= v ? color : "#0001" }} />
                                ))}
                              </div>
                              <div style={{ fontSize: 10, color: "#666", marginTop: 2 }}>{v}/5</div>
                            </div>
                          );
                        })}
                      </div>
                    )}
                    {last && last.comparison && (
                      <div style={{
                        marginTop: 8, fontSize: 11, padding: "4px 8px", borderRadius: 6,
                        background:
                          last.comparison.verdict === "better"  ? "#2DAF6B22" :
                          last.comparison.verdict === "worse"   ? "#B4488C22" : "#0001",
                        color:
                          last.comparison.verdict === "better"  ? "#196C42" :
                          last.comparison.verdict === "worse"   ? "#742855" : "#555",
                      }}>
                        {last.comparison.verdict === "better"  ? "↑ Better than last best — " :
                         last.comparison.verdict === "worse"   ? "↓ Worse than last best — " :
                                                                 "≈ Similar to last best — "}
                        {last.comparison.reason}
                      </div>
                    )}
                  </div>
                )}
                <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                  {!isRunning && !isGrading && (
                    <button onClick={() => action(p.id, "start")} style={btn("good")}>▶ Run</button>
                  )}
                  {isRunning && (
                    <>
                      <button onClick={() => action(p.id, "pause")} style={btn("warn")}>⏸ Pause</button>
                      <button onClick={() => action(p.id, "stop")} style={btn("muted")}>⏹ Stop & grade</button>
                    </>
                  )}
                  {isPaused && (
                    <>
                      <button onClick={() => action(p.id, "resume")} style={btn("good")}>▶ Resume</button>
                      <button onClick={() => action(p.id, "stop")} style={btn("muted")}>⏹ Stop & grade</button>
                    </>
                  )}
                  <button onClick={() => setOpenId(openId === p.id ? null : p.id)} style={btn("muted")}>
                    {openId === p.id ? "Hide" : "Open"}
                  </button>
                  <button onClick={() => remove(p.id)} style={btn("danger")} disabled={isRunning || isGrading}>🗑</button>
                </div>

                {/* Detail expansion: transcript + grade history */}
                {openId === p.id && detail && (() => {
                  // Build lookup of judge complaints keyed by turn index
                  const lastDetail = (detail.grades && detail.grades.length) ? detail.grades[detail.grades.length - 1] : null;
                  const issuesByTurn = {};
                  if (lastDetail && Array.isArray(lastDetail.issues)) {
                    for (const it of lastDetail.issues) (issuesByTurn[it.turnIndex] ||= []).push(it.issue);
                  }
                  return (
                  <div style={{ marginTop: 14, paddingTop: 14, borderTop: "1px dashed var(--ds-line, #e5e5e5)" }}>
                    <div style={{ fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: 0.6, color: "#888", marginBottom: 8 }}>Latest transcript</div>
                    <div style={{ maxHeight: 240, overflowY: "auto", background: "#0000000a", borderRadius: 8, padding: 10, marginBottom: 14 }}>
                      {(detail.transcript || []).length === 0 ? (
                        <div style={{ color: "#888", fontSize: 12 }}>(no messages yet — start the persona to see the conversation)</div>
                      ) : (
                        (detail.transcript || []).map((m, i) => {
                          const teachKey = `${detail.id}-${i}`;
                          const isOpen   = teachOpen === teachKey;
                          return (
                            <div key={i} style={{
                              margin: "6px 0", padding: "6px 10px", borderRadius: 6,
                              background: m.role === "customer" ? "#3B7CFF22" : "#2DAF6B22",
                              fontSize: 12, lineHeight: 1.4, position: "relative",
                            }}>
                              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "start", gap: 8 }}>
                                <b style={{ fontSize: 10, opacity: 0.7 }}>
                                  {m.role === "customer" ? "👤 " + p.name : "🤖 " + (a ? a.name : "Agent")}
                                </b>
                                {m.role === "agent" && !isOpen && (
                                  <button
                                    onClick={() => { setTeachOpen(teachKey); setTeachNote(""); }}
                                    title="Teach the agent what was wrong with this reply"
                                    style={{
                                      background: "transparent", border: "none", cursor: "pointer",
                                      fontSize: 11, color: "#666", padding: 0,
                                    }}
                                  >👎 teach</button>
                                )}
                              </div>
                              <div dir="auto">{m.content}</div>
                              {issuesByTurn[i] && issuesByTurn[i].length > 0 && (
                                <div style={{ marginTop: 6, padding: "4px 8px", borderRadius: 4, background: "#B4488C18", borderLeft: "3px solid #B4488C", fontSize: 11, color: "#742855" }}>
                                  <b style={{ fontSize: 9, textTransform: "uppercase", letterSpacing: 0.4, opacity: 0.8 }}>Judge flagged this turn:</b>
                                  {issuesByTurn[i].map((iss, ix) => <div key={ix} style={{ marginTop: 2 }}>• {iss}</div>)}
                                </div>
                              )}
                              {isOpen && (
                                <div style={{ marginTop: 8, display: "flex", gap: 6, alignItems: "stretch" }}>
                                  <input
                                    autoFocus
                                    type="text"
                                    placeholder="What was wrong / what should it have done instead?"
                                    value={teachNote}
                                    onChange={(e) => setTeachNote(e.target.value)}
                                    onKeyDown={(e) => { if (e.key === "Enter") submitTeach(detail, i); if (e.key === "Escape") { setTeachOpen(null); setTeachNote(""); } }}
                                    style={{ flex: 1, padding: "6px 10px", border: "1px solid var(--ds-line, #c0c0c0)", borderRadius: 6, fontSize: 12 }}
                                  />
                                  <button
                                    onClick={() => submitTeach(detail, i)}
                                    disabled={teachBusy}
                                    style={{ ...btn("good"), opacity: teachBusy ? 0.5 : 1 }}
                                  >{teachBusy ? "…" : "Save"}</button>
                                  <button
                                    onClick={() => { setTeachOpen(null); setTeachNote(""); }}
                                    style={btn("muted")}
                                  >Cancel</button>
                                </div>
                              )}
                            </div>
                          );
                        })
                      )}
                    </div>
                    {/* Grade history */}
                    {(detail.grades || []).length > 0 && (
                      <>
                        <div style={{ fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: 0.6, color: "#888", marginBottom: 8 }}>Grade history</div>
                        <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                          {(detail.grades || []).slice().reverse().map((g) => (
                            <div key={g.id} style={{ display: "flex", alignItems: "center", gap: 10, fontSize: 12 }}>
                              <div style={{
                                fontWeight: 700, fontSize: 14, minWidth: 40,
                                color: g.score >= 7 ? "#2DAF6B" : g.score >= 5 ? "#E85D1A" : "#B4488C",
                              }}>{g.score}/10</div>
                              <div style={{ flex: 1, color: "var(--ds-muted, #555)" }}>{g.feedback}</div>
                              <button
                                onClick={() => promoteGrade(detail, g)}
                                disabled={promoteBusy === g.id || !g.feedback}
                                title="Force this grade's feedback into the agent's learnings, bypassing the auto-learn score floor"
                                style={{
                                  background: "transparent", border: "1px solid var(--ds-line, #c0c0c0)",
                                  borderRadius: 4, padding: "3px 8px", fontSize: 10, cursor: "pointer",
                                  color: "#666", whiteSpace: "nowrap",
                                  opacity: promoteBusy === g.id ? 0.5 : 1,
                                }}
                              >{promoteBusy === g.id ? "…" : "→ Lesson"}</button>
                              <div style={{ color: "#aaa", fontSize: 10 }}>{relativeAt(g.at)}</div>
                            </div>
                          ))}
                        </div>
                      </>
                    )}
                    {/* Reset */}
                    {!isRunning && !isGrading && (detail.transcript || []).length > 0 && (
                      <button
                        onClick={() => fetch(`${PERSONAS_SERVER()}/personas/${p.id}/reset`, { method: "POST" }).then(reload)}
                        style={{ ...btn("muted"), marginTop: 12, fontSize: 11 }}
                      >Clear transcript</button>
                    )}
                  </div>
                  );
                })()}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function btn(kind) {
  const styles = {
    good:   { background: "#2DAF6B", color: "white", border: "none" },
    warn:   { background: "#FFCA7D", color: "#7A4400", border: "none" },
    muted:  { background: "transparent", color: "var(--ds-text, #111)", border: "1px solid var(--ds-line, #e5e5e5)" },
    danger: { background: "transparent", color: "#B4488C", border: "1px solid #B4488C44" },
  };
  return { ...styles[kind], padding: "6px 12px", borderRadius: 6, cursor: "pointer", fontSize: 12, fontWeight: 600 };
}

function chipStyle(color) {
  return {
    fontSize: 10,
    lineHeight: "18px",
    height: 18,
    padding: "0 7px",
    borderRadius: 99,
    background: color + "18",
    color,
    fontWeight: 700,
    textTransform: "uppercase",
    letterSpacing: 0.3,
  };
}

function relativeAt(iso) {
  if (!iso) return "";
  const ms = Date.now() - new Date(iso).getTime();
  const min = Math.floor(ms / 60000);
  if (min < 1) return "now";
  if (min < 60) return `${min}m ago`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr}h ago`;
  return `${Math.floor(hr / 24)}d ago`;
}

window.PersonasTab = PersonasTab;
