// Agent activity rendering — the transparency layer.
//
// Three components:
//   <MessageBody text={...} />     — splits prose vs "★ Insight" segments
//   <ActivityTrail activity={...}/>— timeline of tool calls / subagents / artifacts
//   <CDBriefCard brief={...} />    — structured creative-direction brief
// All driven from real SSE events; no agent-specific or tool-specific hardcoding.

const { useState, useMemo } = React;

// (Insight regex parser removed per "no regex for structured output" rule.
// If we need an insight component again, add an `emit_insight` MCP tool the
// agent calls deliberately, with its own SSE event + component — never
// retroactively pattern-match the model's text stream.)

// ─────────────────────────────────────────────────────────────────────
// ActivityTrail — timeline of tool calls / subagents / artifacts
// ─────────────────────────────────────────────────────────────────────

function ActivityTrail({ activity }) {
  // Always render — empty state shows "no tools used this turn" so the user
  // can distinguish "agent did silent work" from "trail isn't loading."
  const arr = Array.isArray(activity) ? activity : [];

  if (arr.length === 0) {
    return (
      <div style={{ marginTop: 10 }}>
        <div className="label label-moss" style={{ marginBottom: 4 }}>
          Activity · 0
        </div>
        <div style={{
          fontSize: 11, color: 'var(--ink-mute)', fontStyle: 'italic',
          padding: '4px 8px', borderRadius: 6,
          background: 'var(--bg-2)', border: '1px solid var(--line-soft)',
          fontFamily: 'var(--font-mono)', letterSpacing: '.04em',
        }}>
          No tools or subagents used in this turn — agent answered from prompt + skills only.
        </div>
      </div>
    );
  }

  // Walk the activity array, building a tree where subagent_start opens an
  // indented child group and subagent_stop closes it.
  const root = [];
  const stack = [{ children: root, agent_type: null }];
  for (const e of arr) {
    if (e.kind === 'subagent_start') {
      const node = { kind: 'subagent', agent_type: e.agent_type, agent_id: e.agent_id, children: [] };
      stack[stack.length - 1].children.push(node);
      stack.push({ children: node.children, agent_type: e.agent_type });
    } else if (e.kind === 'subagent_stop') {
      if (stack.length > 1) stack.pop();
    } else {
      stack[stack.length - 1].children.push({ kind: 'leaf', entry: e });
    }
  }
  return (
    <div style={{ marginTop: 10 }}>
      <div className="label label-moss" style={{ marginBottom: 6 }}>
        Activity · {arr.length}
      </div>
      <ActivityNodes nodes={root} depth={0} />
    </div>
  );
}

function ActivityNodes({ nodes, depth }) {
  return (
    <div style={{
      display: 'flex', flexDirection: 'column', gap: 4,
      paddingLeft: depth ? 14 : 0,
      borderLeft: depth ? '1px dashed var(--line-soft)' : 'none',
      marginLeft: depth ? 4 : 0,
    }}>
      {nodes.map((n, i) =>
        n.kind === 'subagent'
          ? <SubagentBlock key={i} node={n} depth={depth + 1} />
          : <ActivityLeaf key={i} entry={n.entry} />
      )}
    </div>
  );
}

function SubagentBlock({ node, depth }) {
  const [open, setOpen] = useState(true);
  const label = node.agent_type
    ? node.agent_type.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase())
    : 'subagent';
  return (
    <div>
      <button
        type="button"
        onClick={() => setOpen(v => !v)}
        className="btn-reset"
        style={{
          display: 'flex', alignItems: 'center', gap: 6,
          padding: '4px 8px', borderRadius: 6,
          background: 'var(--bg-2)', border: '1px solid var(--line-soft)',
          fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.04em',
          color: 'var(--ink-soft)', cursor: 'pointer',
        }}
      >
        <span>{open ? '▾' : '▸'}</span>
        <span>→ {label}</span>
      </button>
      {open && node.children.length > 0 && (
        <div style={{ marginTop: 4 }}>
          <ActivityNodes nodes={node.children} depth={depth} />
        </div>
      )}
    </div>
  );
}

function ActivityLeaf({ entry }) {
  if (entry.kind === 'tool') return <ToolEntry entry={entry} />;
  if (entry.kind === 'artifact') return <ArtifactEntry entry={entry} />;
  return null;
}

function ToolEntry({ entry }) {
  const [open, setOpen] = useState(false);
  const hasResult = entry.result !== null && entry.result !== undefined;
  const isJson = useMemo(() => {
    if (!hasResult) return false;
    const r = (entry.result || '').trim();
    return r.startsWith('{') || r.startsWith('[');
  }, [hasResult, entry.result]);
  const prettyResult = useMemo(() => {
    if (!hasResult) return '';
    if (!isJson) return entry.result;
    try {
      return JSON.stringify(JSON.parse(entry.result), null, 2);
    } catch {
      return entry.result;
    }
  }, [hasResult, isJson, entry.result]);

  return (
    <div style={{
      borderRadius: 6, border: '1px solid var(--line-soft)',
      background: 'var(--card-solid)', overflow: 'hidden',
    }}>
      <button
        type="button"
        onClick={() => setOpen(v => !v)}
        className="btn-reset"
        style={{
          width: '100%', textAlign: 'left',
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '5px 9px',
          background: 'transparent', cursor: 'pointer',
        }}
      >
        <span style={{ fontSize: 11, color: 'var(--ink-mute)' }}>{open ? '▾' : '▸'}</span>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.04em',
          padding: '1px 5px', borderRadius: 3,
          background: 'var(--moss-glow)', color: 'var(--moss-deep)',
        }}>{entry.slug || 'tool'}</span>
        {entry.name && entry.name !== entry.slug && (
          <span style={{ fontSize: 11.5, color: 'var(--ink)' }}>{entry.name}</span>
        )}
        <span style={{ flex: 1 }} />
        {entry.result_size != null && (
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--ink-mute)',
          }}>{formatBytes(entry.result_size)}</span>
        )}
        {!hasResult && (
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--ink-mute)', opacity: 0.7,
          }}>running…</span>
        )}
      </button>
      {open && (
        <div style={{ padding: '6px 10px 10px 10px', borderTop: '1px solid var(--line-soft)' }}>
          {entry.description && (
            <div style={{
              fontSize: 11, color: 'var(--ink-soft)', marginBottom: 6, fontStyle: 'italic',
            }}>{entry.description}</div>
          )}
          {hasResult ? (
            <pre style={{
              margin: 0, padding: '6px 8px', borderRadius: 4,
              background: 'var(--bg-2)', color: 'var(--ink)',
              fontFamily: 'var(--font-mono)', fontSize: 10.5, lineHeight: 1.45,
              whiteSpace: 'pre-wrap', wordBreak: 'break-word',
              maxHeight: 320, overflow: 'auto',
            }}>{prettyResult}</pre>
          ) : (
            <div style={{ fontSize: 11, color: 'var(--ink-mute)', fontStyle: 'italic' }}>
              No result yet.
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function ActivityArtifactEntry({ entry }) {
  if (entry.kind_subtype === 'cd_brief') return <CDBriefCard brief={entry.data} />;
  if (entry.kind_subtype === 'ask_user_question') return <AskUserQuestionCard data={entry.data} />;
  return <GenericArtifact entry={entry} />;
}
const ArtifactEntry = ActivityArtifactEntry;

// ─────────────────────────────────────────────────────────────────────
// AskUserQuestionCard — UI for the SDK's built-in AskUserQuestion tool
// ─────────────────────────────────────────────────────────────────────
//
// The CD calls AskUserQuestion → backend's can_use_tool callback pauses
// the SDK query and pushes the structured questions to SSE. This card
// renders them as a UI element. On submit, POSTs to /chat/v2/answer
// which resolves the backend's pending future, which lets the SDK resume
// streaming on the SAME open SSE connection (no new turn).
//
// Supports 1-4 questions per card with 2-4 options each, multiSelect and
// single-select. After submission, the card locks (visual confirmation +
// disabled controls).

function AskUserQuestionCard({ data }) {
  // selections: { [questionText]: string | string[] }
  const [selections, setSelections] = useState({});
  const [submitted, setSubmitted] = useState(false);
  const [error, setError] = useState(null);

  if (!data || !Array.isArray(data.questions) || data.questions.length === 0) {
    return null;
  }

  const allAnswered = data.questions.every(q => {
    const s = selections[q.question];
    if (q.multiSelect) return Array.isArray(s) && s.length > 0;
    return typeof s === 'string' && s.length > 0;
  });

  const togglePick = (q, label) => {
    if (submitted) return;
    setSelections(prev => {
      const next = { ...prev };
      if (q.multiSelect) {
        const arr = Array.isArray(next[q.question]) ? [...next[q.question]] : [];
        const i = arr.indexOf(label);
        if (i >= 0) arr.splice(i, 1); else arr.push(label);
        next[q.question] = arr;
      } else {
        next[q.question] = label;
      }
      return next;
    });
  };

  const submit = async () => {
    if (!allAnswered || submitted) return;
    // Build the answers map per the SDK's expected shape:
    //   { [questionText]: "Selected Label" }   (single-select)
    //   { [questionText]: "Label A, Label B" } (multi-select; comma-joined)
    const answers = {};
    for (const q of data.questions) {
      const s = selections[q.question];
      if (q.multiSelect) {
        answers[q.question] = (Array.isArray(s) ? s : []).join(', ');
      } else {
        answers[q.question] = s || '';
      }
    }
    try {
      const res = await fetch('http://localhost:8001/chat/v2/answer', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ thread_id: data.thread_id, qid: data.qid, answers }),
      });
      if (!res.ok) {
        const errBody = await res.json().catch(() => ({}));
        throw new Error(errBody.detail || `HTTP ${res.status}`);
      }
      setSubmitted(true);
    } catch (e) {
      setError(e.message || 'Failed to submit answer');
      console.error('AskUserQuestion submit failed', e);
    }
  };

  return (
    <div style={{
      marginTop: 4, borderRadius: 8,
      border: '1px solid var(--line)', background: 'var(--card-solid)',
      overflow: 'hidden',
    }}>
      <div style={{
        padding: '8px 10px',
        background: 'var(--moss-glow)',
        display: 'flex', alignItems: 'center', gap: 8,
        fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em',
        textTransform: 'uppercase', color: 'var(--moss-deep)',
      }}>
        <span>Pick to continue</span>
        <span style={{ flex: 1 }} />
        {submitted && <span>↳ sent</span>}
      </div>
      <div style={{ padding: '12px', display: 'flex', flexDirection: 'column', gap: 14 }}>
        {data.questions.map((q, qi) => {
          const sel = selections[q.question];
          return (
            <div key={qi} style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {q.header && (
                <div style={{
                  fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em',
                  textTransform: 'uppercase', color: 'var(--ink-soft)',
                }}>{q.header}{q.multiSelect ? ' · multi' : ''}</div>
              )}
              {q.question && (
                <div style={{
                  fontFamily: 'var(--font-serif)', fontSize: 14, lineHeight: 1.45, color: 'var(--ink)',
                }}>{q.question}</div>
              )}
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 2 }}>
                {(q.options || []).map((opt, oi) => {
                  const label = opt && opt.label ? opt.label : '';
                  const desc = opt && opt.description ? opt.description : '';
                  const active = q.multiSelect
                    ? Array.isArray(sel) && sel.includes(label)
                    : sel === label;
                  return (
                    <button
                      key={oi}
                      type="button"
                      onClick={() => togglePick(q, label)}
                      disabled={submitted}
                      className="btn-reset"
                      title={desc || undefined}
                      style={{
                        padding: '8px 12px',
                        borderRadius: 8,
                        border: `1px solid ${active ? 'var(--moss-deep)' : 'var(--line)'}`,
                        background: active ? 'var(--moss-deep)' : 'var(--card-solid)',
                        color: active ? 'var(--bg)' : 'var(--ink)',
                        fontSize: 12,
                        fontFamily: 'var(--font-serif)',
                        cursor: submitted ? 'default' : 'pointer',
                        textAlign: 'left',
                        maxWidth: 380,
                        transition: 'all .15s',
                        display: 'flex', flexDirection: 'column', gap: 2,
                        opacity: submitted && !active ? 0.55 : 1,
                      }}
                    >
                      <span style={{ fontWeight: 500 }}>{label}</span>
                      {desc && (
                        <span style={{
                          fontSize: 10.5, lineHeight: 1.4,
                          color: active ? 'var(--bg)' : 'var(--ink-soft)',
                          opacity: 0.85,
                        }}>{desc}</span>
                      )}
                    </button>
                  );
                })}
              </div>
            </div>
          );
        })}
        {!submitted && (
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 4 }}>
            <button
              type="button"
              onClick={submit}
              disabled={!allAnswered}
              className="btn-reset"
              style={{
                padding: '8px 16px', borderRadius: 8,
                background: allAnswered ? 'var(--moss-deep)' : 'var(--bg-2)',
                color: allAnswered ? 'var(--bg)' : 'var(--ink-mute)',
                fontSize: 12, cursor: allAnswered ? 'pointer' : 'default',
                transition: 'all .15s',
              }}
            >
              Continue
            </button>
            {error && (
              <span style={{ fontSize: 11, color: 'var(--accent-warm)' }}>{error}</span>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────
// CDBriefCard — structured creative-direction brief
// ─────────────────────────────────────────────────────────────────────

function CDBriefCard({ brief }) {
  const [open, setOpen] = useState(false);
  if (!brief || typeof brief !== 'object') return null;
  const vision = brief.vision || {};
  const specs = brief.specifications || {};
  const refs = brief.references || {};
  return (
    <div style={{
      marginTop: 4, borderRadius: 8,
      border: '1px solid var(--line)', background: 'var(--card-solid)',
      overflow: 'hidden',
    }}>
      <button
        type="button"
        onClick={() => setOpen(v => !v)}
        className="btn-reset"
        style={{
          width: '100%', textAlign: 'left',
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '8px 10px',
          background: 'var(--moss-glow)', cursor: 'pointer',
        }}
      >
        <span style={{ fontSize: 12 }}>{open ? '▾' : '▸'}</span>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em',
          textTransform: 'uppercase', color: 'var(--moss-deep)',
        }}>CDBrief captured</span>
        {vision.strategic_move && (
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 9, padding: '1px 5px',
            borderRadius: 3, background: 'var(--bg)', color: 'var(--ink-soft)',
            border: '1px solid var(--line-soft)',
          }}>{vision.strategic_move}</span>
        )}
        <span style={{ flex: 1 }} />
        {brief.felt_sense_check && (
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--ink-mute)',
          }}>{brief.felt_sense_check}</span>
        )}
      </button>
      {open && (
        <div style={{
          padding: '10px 12px',
          display: 'flex', flexDirection: 'column', gap: 12,
          fontSize: 12, lineHeight: 1.5, color: 'var(--ink)',
        }}>
          {vision.story_being_told && <CDSection label="Story being told" value={vision.story_being_told} />}
          {vision.emotional_register && <CDSection label="Emotional register" value={vision.emotional_register} />}
          {Array.isArray(vision.exclusions) && vision.exclusions.length > 0 && (
            <CDList label="Exclusions" items={vision.exclusions} />
          )}
          {specs.subject_quality && <CDSection label="Subject quality" value={specs.subject_quality} />}
          {specs.casting_logic && (
            <CDSection label="Casting" value={
              [specs.casting_logic.specificity_notes, specs.casting_logic.alignment, specs.casting_logic.opposition]
                .filter(Boolean).join(' · ')
            } />
          )}
          {specs.environment && specs.environment.place_specificity && (
            <CDSection label="Environment" value={specs.environment.place_specificity} />
          )}
          {specs.light && (
            <CDSection label="Light" value={
              [specs.light.direction, specs.light.quality, specs.light.temperature, specs.light.source]
                .filter(Boolean).join(' / ')
            } />
          )}
          {Array.isArray(specs.second_look_details) && specs.second_look_details.length > 0 && (
            <CDList label="Second-look details" items={specs.second_look_details} />
          )}
          {specs.calibrated_risk && <CDSection label="Calibrated risk" value={specs.calibrated_risk} />}
          {Array.isArray(refs.cross_domain_pulled) && refs.cross_domain_pulled.length > 0 && (
            <div>
              <div className="label label-moss" style={{ marginBottom: 4 }}>References pulled</div>
              <ul style={{ margin: 0, paddingLeft: 18, color: 'var(--ink-soft)' }}>
                {refs.cross_domain_pulled.map((r, i) => (
                  <li key={i}>
                    <span style={{ color: 'var(--ink)' }}>{r.reference}</span>
                    {r.for_what_quality && <span style={{ color: 'var(--ink-mute)' }}> — {r.for_what_quality}</span>}
                  </li>
                ))}
              </ul>
            </div>
          )}
          {Array.isArray(refs.in_category_avoided) && refs.in_category_avoided.length > 0 && (
            <CDList label="In-category avoided" items={refs.in_category_avoided} />
          )}
          {Array.isArray(brief.open_questions) && brief.open_questions.length > 0 && (
            <CDList label="Open questions" items={brief.open_questions} />
          )}
        </div>
      )}
    </div>
  );
}

function CDSection({ label, value }) {
  return (
    <div>
      <div className="label label-moss" style={{ marginBottom: 3 }}>{label}</div>
      <div>{value}</div>
    </div>
  );
}

function CDList({ label, items }) {
  return (
    <div>
      <div className="label label-moss" style={{ marginBottom: 3 }}>{label}</div>
      <ul style={{ margin: 0, paddingLeft: 18, color: 'var(--ink-soft)' }}>
        {items.map((it, i) => <li key={i}>{it}</li>)}
      </ul>
    </div>
  );
}

function GenericArtifact({ entry }) {
  const [open, setOpen] = useState(false);
  return (
    <div style={{
      borderRadius: 6, border: '1px solid var(--line-soft)',
      background: 'var(--card-solid)', overflow: 'hidden',
    }}>
      <button
        type="button"
        onClick={() => setOpen(v => !v)}
        className="btn-reset"
        style={{
          width: '100%', textAlign: 'left',
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '5px 9px',
          background: 'transparent', cursor: 'pointer',
        }}
      >
        <span style={{ fontSize: 11, color: 'var(--ink-mute)' }}>{open ? '▾' : '▸'}</span>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.06em',
          textTransform: 'uppercase', color: 'var(--ink-soft)',
        }}>{entry.kind_subtype || 'artifact'}</span>
      </button>
      {open && (
        <pre style={{
          margin: 0, padding: '6px 10px', fontFamily: 'var(--font-mono)',
          fontSize: 10.5, color: 'var(--ink)', whiteSpace: 'pre-wrap',
          maxHeight: 320, overflow: 'auto',
        }}>{JSON.stringify(entry.data, null, 2)}</pre>
      )}
    </div>
  );
}

function formatBytes(n) {
  if (n == null) return '';
  if (n < 1024) return `${n}B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
  return `${(n / 1024 / 1024).toFixed(1)}MB`;
}

// ─────────────────────────────────────────────────────────────────────
// TodoListCard — SDK's TodoWrite live progress
// ─────────────────────────────────────────────────────────────────────
//
// The SDK auto-creates todos for complex multi-step tasks. Each TodoWrite
// invocation emits the FULL updated list (replacement, not delta). The
// list contains items like:
//   { content: "Read the brand graph", activeForm: "Reading the brand graph",
//     status: "pending" | "in_progress" | "completed" }
//
// We render at the TOP of the assistant message so the user sees the plan
// + live progress before reading anything else.

function TodoListCard({ todos }) {
  if (!Array.isArray(todos) || todos.length === 0) return null;

  const completed = todos.filter(t => t && t.status === 'completed').length;
  const inProgress = todos.filter(t => t && t.status === 'in_progress').length;
  const total = todos.length;
  const fullyDone = completed === total;

  return (
    <div style={{
      marginBottom: 12,
      borderRadius: 8,
      border: '1px solid var(--line)',
      background: 'var(--card-solid)',
      overflow: 'hidden',
    }}>
      <div style={{
        padding: '7px 10px',
        background: fullyDone ? 'var(--bg-2)' : 'var(--moss-glow)',
        display: 'flex', alignItems: 'center', gap: 8,
        fontFamily: 'var(--font-mono)', fontSize: 9, letterSpacing: '.08em',
        textTransform: 'uppercase',
        color: fullyDone ? 'var(--ink-soft)' : 'var(--moss-deep)',
        borderBottom: '1px solid var(--line-soft)',
      }}>
        <span>Plan · {completed}/{total}</span>
        {inProgress > 0 && (
          <>
            <span style={{ flex: 1 }} />
            <Orbit size={9} />
            <span>working</span>
          </>
        )}
      </div>
      <ul style={{
        margin: 0, padding: '8px 0',
        listStyle: 'none',
        display: 'flex', flexDirection: 'column',
      }}>
        {todos.map((t, i) => {
          if (!t) return null;
          const isDone = t.status === 'completed';
          const isActive = t.status === 'in_progress';
          const text = isActive ? (t.activeForm || t.content || '') : (t.content || '');
          return (
            <li key={i} style={{
              padding: '5px 12px',
              display: 'flex', alignItems: 'flex-start', gap: 8,
              fontSize: 12, lineHeight: 1.45,
              color: isDone ? 'var(--ink-mute)' : (isActive ? 'var(--ink)' : 'var(--ink-soft)'),
            }}>
              <span style={{
                width: 18, height: 18, flexShrink: 0,
                marginTop: 1,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                borderRadius: '50%',
                border: `1.5px solid ${isDone ? 'var(--moss-deep)' : (isActive ? 'var(--moss-deep)' : 'var(--line)')}`,
                background: isDone ? 'var(--moss-deep)' : 'transparent',
                color: isDone ? 'var(--bg)' : 'var(--moss-deep)',
                fontSize: 10, fontWeight: 700,
              }}>
                {isDone ? '✓' : (isActive ? '●' : '')}
              </span>
              <span style={{
                fontWeight: isActive ? 500 : 400,
                textDecoration: isDone ? 'line-through' : 'none',
                opacity: isDone ? 0.7 : 1,
              }}>
                {text}
              </span>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

// Expose to window for views.jsx (Babel-standalone runs files as scripts).
window.ActivityTrail = ActivityTrail;
window.CDBriefCard = CDBriefCard;
window.AskUserQuestionCard = AskUserQuestionCard;
window.TodoListCard = TodoListCard;
