// Chat — conversation list & pane (sidebar, turns, bubbles, composer). Split out of chat.jsx; loads after it.
const { useState: useCvS, useEffect: useCvE, useRef: useCvR, useLayoutEffect: useCvL } = React;

function ConversationsSidebar({ conversations, currentId, open, setOpen, onSwitch, onNew, onDelete }) {
  const width = open ? 240 : 44;
  return (
    <aside style={{
      width, flexShrink: 0, borderRight: '1px solid var(--border-subtle)',
      background: 'var(--bg)', display: 'flex', flexDirection: 'column',
      transition: 'width 160ms cubic-bezier(.2,.8,.2,1)', overflow: 'hidden', minHeight: 0,
    }}>
      {/* Header */}
      <div style={{
        padding: open ? '10px 12px' : 6,
        borderBottom: '1px solid var(--border-subtle)',
        display: 'flex', alignItems: 'center', gap: 6,
      }}>
        {open ? (
          <>
            <span className="t-micro text-tertiary" style={{ flex: 1 }}>Chats</span>
            <button className="btn sm ghost" style={{ padding: '0 6px', height: 24 }} onClick={() => setOpen(false)} title="Collapse">
              <Icons.ChevronL size={12}/>
            </button>
            <button className="btn sm accent" style={{ padding: '0 8px', height: 24 }} onClick={onNew}>
              <Icons.Plus size={11}/> New
            </button>
          </>
        ) : (
          <button className="btn sm accent" style={{ padding: 0, height: 28, width: '100%' }} onClick={onNew} title="New chat">
            <Icons.Plus size={12}/>
          </button>
        )}
      </div>

      {/* Body */}
      <div style={{ flex: 1, overflowY: 'auto', padding: open ? '6px 8px' : 6 }}>
        {open ? (
          conversations.length === 0 ? (
            <div className="t-small text-tertiary" style={{ padding: '18px 10px', textAlign: 'center' }}>No chats yet</div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
              {conversations.map(c => (
                <ConvItem key={c.conversation_id}
                  conv={c}
                  active={c.conversation_id === currentId}
                  onSwitch={() => onSwitch(c.conversation_id)}
                  onDelete={() => onDelete(c.conversation_id)}/>
              ))}
            </div>
          )
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center' }}>
            {conversations.slice(0, 12).map(c => {
              const title = conversationTitle(c);
              const isActive = c.conversation_id === currentId;
              return (
                <button key={c.conversation_id} onClick={() => onSwitch(c.conversation_id)} title={title}
                  style={{
                    width: 30, height: 30, borderRadius: 7,
                    background: isActive ? 'var(--selected)' : 'transparent',
                    color: isActive ? 'var(--text-primary)' : 'var(--text-tertiary)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    transition: 'background 80ms',
                  }}
                  onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = 'var(--hover)'; }}
                  onMouseLeave={e => { if (!isActive) e.currentTarget.style.background = 'transparent'; }}
                >
                  <Icons.Chat size={13}/>
                </button>
              );
            })}
            <button className="btn sm ghost" style={{ padding: 0, width: 30, height: 30, marginTop: 6 }} onClick={() => setOpen(true)} title="Expand">
              <Icons.ChevronR size={12}/>
            </button>
          </div>
        )}
      </div>
    </aside>
  );
}

function ConvItem({ conv, active, onSwitch, onDelete }) {
  const [hover, setHover] = useCvS(false);
  const title = conversationTitle(conv);
  return (
    <div
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      onClick={onSwitch}
      style={{
        display: 'flex', alignItems: 'center', gap: 6,
        padding: '6px 8px', borderRadius: 7, cursor: 'pointer',
        background: active ? 'var(--selected)' : (hover ? 'var(--hover)' : 'transparent'),
        color: active ? 'var(--text-primary)' : 'var(--text-secondary)',
        minHeight: 30,
      }}
    >
      <Icons.Chat size={12} style={{ flexShrink: 0, color: 'var(--text-tertiary)' }}/>
      <span style={{
        flex: 1, minWidth: 0, overflow: 'hidden',
        textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 13,
      }}>{title}</span>
      {hover && (
        <div style={{ display: 'flex', gap: 2, flexShrink: 0 }} onClick={e => e.stopPropagation()}>
          <button className="btn sm ghost" style={{ padding: 0, height: 22, width: 22 }} title="Delete"
            onClick={() => { if (window.confirm(`Delete "${title}"?`)) onDelete(); }}>
            <Icons.Trash size={11}/>
          </button>
        </div>
      )}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// ConversationPane — messages + composer
// ──────────────────────────────────────────────────────────────────────────
function ConversationPane({ turns, effectiveTurnId, latestTurnId, onPin, draft, setDraft, onSend, onStop, onNewChat, streaming, conversation }) {
  const scrollRef = useCvR(null);
  const stickyRef = useCvR(true);
  useCvE(() => {
    const el = scrollRef.current;
    if (!el) return;
    const onScroll = () => {
      stickyRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 48;
    };
    el.addEventListener('scroll', onScroll, { passive: true });
    return () => el.removeEventListener('scroll', onScroll);
  }, []);
  useCvL(() => {
    const el = scrollRef.current;
    if (el && stickyRef.current) el.scrollTop = el.scrollHeight;
  });

  // When switching conversations, force a scroll-to-bottom reset
  useCvE(() => {
    stickyRef.current = true;
    const el = scrollRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [conversation && conversation.id]);

  return (
    <>
      {/* Header */}
      <div style={{
        padding: '14px 20px', borderBottom: '1px solid var(--border-subtle)',
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <Icons.Chat size={16} style={{ color: 'var(--accent-text)', flexShrink: 0 }}/>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="t-h3" style={{ margin: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {(conversation && conversation.title) || 'Chat'}
          </div>
          <div className="mono-sm mono text-tertiary" style={{ marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {conversation && conversation.conversationId
              ? `conversation · ${conversation.conversationId}`
              : 'new conversation'}
          </div>
        </div>
        <button className="btn sm" onClick={onNewChat}>
          <Icons.Plus size={12}/> New chat
        </button>
      </div>

      {/* Messages */}
      <div ref={scrollRef} style={{ flex: 1, overflowY: 'auto', padding: '20px 20px 8px' }}>
        {turns.length === 0 ? (
          <EmptyChat onExample={(q) => onSend(q)} sendDisabled={streaming}/>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            {turns.map(t => (
              <TurnBlock key={t.id} turn={t}
                pinned={t.id === effectiveTurnId}
                isLatest={t.id === latestTurnId}
                onPin={() => onPin(t.id)}/>
            ))}
          </div>
        )}
      </div>

      <Composer draft={draft} setDraft={setDraft} onSend={onSend} onStop={onStop} streaming={streaming}/>
    </>
  );
}

function EmptyChat({ onExample, sendDisabled }) {
  const examples = [
    'What was East region GMV last quarter?',
    'New user retention trend over the last 30 days',
    'Compare Q1 GMV between East and South regions',
  ];
  return (
    <div style={{ padding: '24px 8px', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center', gap: 20 }}>
      <div style={{
        width: 48, height: 48, borderRadius: 12,
        background: 'var(--accent-muted)', color: 'var(--accent-text)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <Icons.Chat size={22}/>
      </div>
      <div>
        <div className="t-h2">Ask anything about your data</div>
        <div className="text-secondary t-body" style={{ marginTop: 6 }}>
          Queries stream step-by-step. The right panel shows payloads, sources, and the full trace.
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8, width: '100%', maxWidth: 460 }}>
        {examples.map(q => (
          <button key={q} disabled={sendDisabled} onClick={() => onExample(q)}
            className="card"
            style={{ textAlign: 'left', padding: '12px 14px', fontSize: 13, cursor: 'pointer' }}
            onMouseEnter={e => e.currentTarget.style.background = 'var(--hover)'}
            onMouseLeave={e => e.currentTarget.style.background = 'var(--surface)'}
          >
            <span style={{ color: 'var(--text-tertiary)', marginRight: 8 }}>→</span>{q}
          </button>
        ))}
      </div>
    </div>
  );
}

function TurnBlock({ turn, pinned, isLatest, onPin }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      <UserBubble text={turn.query}/>
      <AssistantBubble turn={turn} pinned={pinned} isLatest={isLatest} onPin={onPin}/>
    </div>
  );
}

function UserBubble({ text }) {
  return (
    <div style={{ display: 'flex', gap: 10, alignItems: 'flex-start' }}>
      <div style={{
        width: 24, height: 24, borderRadius: '50%', flexShrink: 0,
        background: 'linear-gradient(135deg, var(--accent), var(--accent-hover))',
        color: 'var(--on-accent)', fontSize: 10, fontWeight: 600,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>MK</div>
      <div style={{ flex: 1, minWidth: 0, fontSize: 14, lineHeight: '22px', color: 'var(--text-primary)', paddingTop: 2, whiteSpace: 'pre-wrap' }}>
        {text}
      </div>
    </div>
  );
}

function AssistantBubble({ turn, pinned, isLatest, onPin }) {
  const streaming = turn.status === 'streaming';
  const error = turn.status === 'error';
  const resp = turn.response;
  return (
    <div onClick={onPin}
      style={{
        display: 'flex', gap: 10, alignItems: 'flex-start',
        padding: '10px 12px',
        borderRadius: 10,
        background: pinned ? 'var(--selected)' : 'var(--surface)',
        border: '1px solid ' + (pinned ? 'var(--border)' : 'var(--border-subtle)'),
        cursor: 'pointer',
        transition: 'background 100ms, border-color 100ms',
      }}
      onMouseEnter={e => { if (!pinned) e.currentTarget.style.background = 'var(--hover)'; }}
      onMouseLeave={e => { if (!pinned) e.currentTarget.style.background = 'var(--surface)'; }}
    >
      <div style={{
        width: 24, height: 24, borderRadius: 6, flexShrink: 0,
        background: 'var(--text-primary)', color: 'var(--bg)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        <Icons.Sparkle size={12}/>
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        {streaming ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, color: 'var(--text-secondary)', fontSize: 14, lineHeight: '22px' }}>
            <SpinnerDot/>
            <TypewriterLabel text={turn.statusLabel || ''}/>
          </div>
        ) : error ? (
          <div style={{ color: 'var(--error)', fontSize: 14 }}>
            <Icons.AlertTri size={13} style={{ marginRight: 6, verticalAlign: -2 }}/>
            {turn.statusLabel || 'Request failed'}
          </div>
        ) : resp ? (
          <div>
            <Markdown text={resp.message}/>
            <div style={{ display: 'flex', gap: 6, marginTop: 8, flexWrap: 'wrap', alignItems: 'center' }}>
              <StatusChip status={resp.status}/>
              {resp.result && resp.result.confidence != null && <ConfidencePill value={resp.result.confidence}/>}
              <ResultSummaryChip turn={turn} pinned={pinned}/>
            </div>
          </div>
        ) : null}
      </div>
    </div>
  );
}

// Inline summary chip under each assistant bubble — click to pin inspector to this turn
function ResultSummaryChip({ turn, pinned }) {
  const resp = turn.response;
  if (!resp) return null;
  const payloads = (resp.result && resp.result.payloads) || [];
  const sources  = (resp.result && resp.result.sources)  || [];
  const steps    = resp.steps || [];
  return (
    <span
      className="chip"
      style={{
        background: pinned ? 'var(--accent-muted)' : 'var(--hover)',
        color:      pinned ? 'var(--accent-text)'  : 'var(--text-secondary)',
        border: '1px solid ' + (pinned ? 'transparent' : 'var(--border-subtle)'),
        gap: 8, fontSize: 11,
      }}
      title={pinned ? 'Viewing in inspector' : 'Click turn to view in inspector'}
    >
      {payloads.length > 0 && (
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>
          <Icons.Box size={10}/> {payloads.length}
        </span>
      )}
      {sources.length > 0 && (
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>
          <Icons.Link size={10}/> {sources.length}
        </span>
      )}
      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3 }}>
        <Icons.Activity size={10}/> {steps.length}
      </span>
      {pinned && <span style={{ marginLeft: 2, fontWeight: 500 }}>· viewing →</span>}
    </span>
  );
}

function SpinnerDot() {
  return (
    <span style={{ width: 10, height: 10, display: 'inline-block', flexShrink: 0 }}>
      <svg viewBox="0 0 24 24" width="10" height="10" style={{ animation: 'parcle-spin 900ms linear infinite' }}>
        <circle cx="12" cy="12" r="9" fill="none" stroke="var(--border)" strokeWidth="3"/>
        <path d="M12 3a9 9 0 019 9" fill="none" stroke="var(--accent)" strokeWidth="3" strokeLinecap="round"/>
      </svg>
    </span>
  );
}

function StatusChip({ status }) {
  const map = {
    success:  { cls: 'success', label: 'success' },
    clarify:  { cls: 'info',    label: 'clarify' },
    no_data:  { cls: 'warning', label: 'no data' },
    failure:  { cls: 'error',   label: 'failure' },
  };
  const m = map[status] || { cls: '', label: status };
  return <span className={`chip ${m.cls}`}><span className="dot"/> {m.label}</span>;
}

function ConfidencePill({ value }) {
  const pct = Math.round(value * 100);
  return <span className="chip accent">conf {pct}%</span>;
}

function Composer({ draft, setDraft, onSend, onStop, streaming }) {
  const ref = useCvR(null);
  useCvE(() => {
    const el = ref.current;
    if (!el) return;
    el.style.height = 'auto';
    el.style.height = Math.min(el.scrollHeight, 160) + 'px';
  }, [draft]);
  const onKey = (e) => {
    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); onSend(); }
  };
  return (
    <div style={{ borderTop: '1px solid var(--border-subtle)', padding: 16, background: 'var(--bg)' }}>
      <div style={{
        border: '1px solid var(--border)', borderRadius: 10, background: 'var(--surface)',
        padding: '10px 12px', display: 'flex', gap: 8, alignItems: 'flex-end',
      }}>
        <textarea
          ref={ref} rows={1} value={draft}
          placeholder={streaming ? 'Streaming…' : 'Ask about your data.  ⌘/Ctrl + Enter to send'}
          onChange={e => setDraft(e.target.value)}
          onKeyDown={onKey}
          style={{
            flex: 1, border: 'none', outline: 'none', background: 'transparent',
            resize: 'none', fontSize: 14, lineHeight: '22px',
            fontFamily: 'inherit', color: 'var(--text-primary)',
            maxHeight: 160, minHeight: 22,
          }}/>
        {streaming ? (
          <button className="btn sm" onClick={onStop}><Icons.StopCircle size={13}/> Stop</button>
        ) : (
          <button className="btn accent sm" disabled={!draft.trim()} onClick={() => onSend()}>
            <Icons.ArrowR size={13}/> Send
          </button>
        )}
      </div>
      <div style={{ marginTop: 6, display: 'flex', justifyContent: 'space-between' }}>
        <span className="mono-sm mono text-tertiary">
          {chatMockEnabled()
            ? 'mock backend · set window.PARCLE_API_BASE for live'
            : `POST ${(window.PARCLE_API_BASE || '/api')}/skill/ask/progress`}
        </span>
      </div>
    </div>
  );
}
