// screens-support-broadcast.jsx — 客服工单中心 + 站内/聊天广播

const { adminFetch: _supFetch, useAdminPoll: _supPoll } = window.NebulaAdmin;

// ─── helpers ────────────────────────────────────────────────────────────────
function _relTime(iso) {
  if (!iso) return '—';
  const t = new Date(iso).getTime();
  if (!t) return '—';
  const diff = (Date.now() - t) / 1000;
  if (diff < 60) return Math.max(1, Math.floor(diff)) + 's';
  if (diff < 3600) return Math.floor(diff / 60) + 'm';
  if (diff < 86400) return Math.floor(diff / 3600) + 'h';
  return Math.floor(diff / 86400) + 'd';
}
function _fmtTime(iso) {
  if (!iso) return '';
  try { return new Date(iso).toLocaleString('zh-CN', { hour12: false }); } catch (e) { return iso; }
}
function _priColor(p) {
  if (p === 'high' || p === 'urgent') return 'danger';
  if (p === 'medium') return 'warning';
  return 'outline';
}
function _statusColor(s) {
  if (s === 'open' || s === 'pending') return 'warning';
  if (s === 'assigned' || s === 'in_progress') return 'info';
  if (s === 'resolved') return 'success';
  if (s === 'closed') return 'outline';
  return 'outline';
}

// ─── SupportScreen ──────────────────────────────────────────────────────────
// Top-level wrapper: chip tab strip ( 工单 / 实时 chat / 知识库 / 大盘 ) + the
// active sub-view. Original list+conversation+actions lives under 「工单」.
function SupportScreen({ t, push }) {
  const [tab, setTab] = React.useState('tickets');
  const tabs = [
    { id: 'tickets', label: t.tx('工单', 'Tickets') },
    { id: 'chat',    label: t.tx('实时 chat', 'Live Chat') },
    { id: 'kb',      label: t.tx('知识库', 'Knowledge Base') },
    { id: 'dash',    label: t.tx('大盘', 'Dashboard') },
  ];
  return (
    <div className="stack">
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', borderBottom: '1px solid var(--border)', paddingBottom: 10 }}>
        {tabs.map(x => (
          <button key={x.id} className={'chip ' + (tab === x.id ? 'active' : '')} onClick={() => setTab(x.id)}>
            {x.label}
          </button>
        ))}
      </div>
      {tab === 'tickets' && <_SupportTickets t={t} push={push} />}
      {tab === 'chat'    && <_SupportLiveChat t={t} push={push} />}
      {tab === 'kb'      && <_SupportKB t={t} push={push} />}
      {tab === 'dash'    && <_SupportDashboard t={t} push={push} />}
    </div>
  );
}

// Original ticket-list+conversation+actions view, factored out so the
// SupportScreen wrapper can swap tabs without losing existing state shape.
function _SupportTickets({ t, push }) {
  const [statusFilter, setStatusFilter] = React.useState('');
  const [priFilter, setPriFilter] = React.useState('');
  const [catFilter, setCatFilter] = React.useState('');
  const [selectedId, setSelectedId] = React.useState(null);
  const [reply, setReply] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [tick, setTick] = React.useState(0);
  const bump = () => setTick(v => v + 1);

  const ticketsPath = React.useMemo(() => {
    const p = new URLSearchParams();
    if (statusFilter) p.set('status', statusFilter);
    if (priFilter) p.set('priority', priFilter);
    if (catFilter) p.set('category', catFilter);
    p.set('_', String(tick));
    return '/api/support/tickets?' + p.toString();
  }, [statusFilter, priFilter, catFilter, tick]);

  const [ticketsData] = _supPoll(ticketsPath, 8000);
  const [statsData] = _supPoll('/api/support/stats?_=' + tick, 15000);
  const [msgData, setMsgData] = React.useState(null);

  const tickets = (ticketsData && ticketsData.tickets) || [];
  const stats = statsData || {};

  // sort: priority weight desc, updated_at desc
  const priWeight = { urgent: 3, high: 2, medium: 1, low: 0 };
  const sorted = React.useMemo(() => {
    return [...tickets].sort((a, b) => {
      const pa = priWeight[a.priority] || 0;
      const pb = priWeight[b.priority] || 0;
      if (pb !== pa) return pb - pa;
      return new Date(b.updated_at || 0).getTime() - new Date(a.updated_at || 0).getTime();
    });
  }, [tickets]);

  const selected = sorted.find(x => x.id === selectedId) || null;

  // load messages for selected
  React.useEffect(() => {
    if (!selectedId) { setMsgData(null); return; }
    let cancelled = false;
    _supFetch(`/api/support/tickets/${selectedId}/messages`)
      .then(d => { if (!cancelled) setMsgData(d); })
      .catch(e => { if (!cancelled) push(t.tx('加载消息失败: ','Failed to load messages: ') + (e.message || e)); });
    return () => { cancelled = true; };
  }, [selectedId, tick]);

  const doAssign = async () => {
    if (!selected) return;
    setSubmitting(true);
    try {
      await _supFetch(`/api/support/tickets/${selected.id}/assign`, { method: 'POST', body: JSON.stringify({ admin_id: 'self' }) });
      push(t.tx('已接单 ','Assigned ') + selected.code);
      bump();
    } catch (e) { push(t.tx('接单失败: ','Assign failed: ') + (e.message || e)); }
    finally { setSubmitting(false); }
  };
  const doReply = async () => {
    if (!selected || !reply.trim()) return;
    setSubmitting(true);
    try {
      await _supFetch(`/api/support/tickets/${selected.id}/admin-reply`, { method: 'POST', body: JSON.stringify({ content: reply.trim() }) });
      setReply('');
      push(t.tx('已发送回复','Reply sent'));
      bump();
    } catch (e) { push(t.tx('回复失败: ','Reply failed: ') + (e.message || e)); }
    finally { setSubmitting(false); }
  };
  const doResolve = async () => {
    if (!selected) return;
    if (!confirm(t.tx('标记为已解决？','Mark as resolved?'))) return;
    try {
      await _supFetch(`/api/support/tickets/${selected.id}/resolve`, { method: 'POST', body: JSON.stringify({}) });
      push(t.tx('已标记解决','Marked resolved'));
      bump();
    } catch (e) { push(t.tx('操作失败: ','Operation failed: ') + (e.message || e)); }
  };
  const doClose = async () => {
    if (!selected) return;
    if (!confirm(t.tx('关闭工单？关闭后用户无法继续追加消息。','Close ticket? User will not be able to add more messages.'))) return;
    try {
      await _supFetch(`/api/support/tickets/${selected.id}/close`, { method: 'POST', body: JSON.stringify({}) });
      push(t.tx('已关闭工单','Ticket closed'));
      bump();
    } catch (e) { push(t.tx('操作失败: ','Operation failed: ') + (e.message || e)); }
  };

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('客服工单中心','Support Ticket Center')}</h1>
          <div className="sub">{t.tx('用户工单 · 实时对话 · 接单/解决/关闭','User tickets · Live chat · Assign / Resolve / Close')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={bump}>{t.tx('刷新','Refresh')}</button>
        </div>
      </div>

      {/* stats strip */}
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 18, flexWrap: 'wrap', alignItems: 'center' }}>
          <_StatCell label={t.tx('今日新增','New Today')} value={stats.today_new ?? '—'} />
          <div className="divider-v" style={{ height: 30 }} />
          <_StatCell label={t.tx('待处理高优','High-Pri Pending')} value={stats.high_priority_unhandled ?? '—'} color="var(--danger)" />
          <div className="divider-v" style={{ height: 30 }} />
          <_StatCell label={t.tx('未关闭总数','Open Total')} value={stats.total_open ?? '—'} />
          <div className="divider-v" style={{ height: 30 }} />
          <_StatCell label={t.tx('平均响应','Avg Response')} value={stats.avg_response_min != null ? stats.avg_response_min + ' min' : '—'} />
          <div className="divider-v" style={{ height: 30 }} />
          <_StatCell label={t.tx('平均解决','Avg Resolve')} value={stats.avg_resolve_min != null ? stats.avg_resolve_min + ' min' : '—'} />
        </div>
      </div>

      {/* 3-pane layout */}
      <div style={{ display: 'grid', gridTemplateColumns: '320px 1fr 280px', gap: 14, minHeight: 520 }}>
        {/* left: tickets */}
        <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
          <div className="card-h"><h3>{t.tx('工单','Tickets')}</h3><span className="meta">{sorted.length}</span></div>
          <div style={{ padding: '8px 10px', borderBottom: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: 6 }}>
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
              {['', 'open', 'assigned', 'resolved', 'closed'].map(s => (
                <button key={'s' + s} className={'chip ' + (statusFilter === s ? 'active' : '')} style={{ fontSize: 10 }} onClick={() => setStatusFilter(s)}>
                  {s === '' ? t.tx('全部状态','All Status') : s}
                </button>
              ))}
            </div>
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
              {['', 'urgent', 'high', 'medium', 'low'].map(p => (
                <button key={'p' + p} className={'chip ' + (priFilter === p ? 'active' : '')} style={{ fontSize: 10 }} onClick={() => setPriFilter(p)}>
                  {p === '' ? t.tx('全部优先级','All Priority') : p}
                </button>
              ))}
            </div>
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
              {['', 'deposit', 'withdraw', 'account', 'game', 'other'].map(c => (
                <button key={'c' + c} className={'chip ' + (catFilter === c ? 'active' : '')} style={{ fontSize: 10 }} onClick={() => setCatFilter(c)}>
                  {c === '' ? t.tx('全部类目','All Categories') : c}
                </button>
              ))}
            </div>
          </div>
          <div style={{ overflowY: 'auto', flex: 1, maxHeight: 600 }}>
            {sorted.length === 0 && <div style={{ padding: 18, fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('没有符合条件的工单','No matching tickets')}</div>}
            {sorted.map(tk => {
              const sel = tk.id === selectedId;
              return (
                <div key={tk.id}
                     onClick={() => setSelectedId(tk.id)}
                     style={{ padding: '10px 12px', borderBottom: '1px solid var(--border)', cursor: 'pointer', background: sel ? 'var(--bg-inset)' : '' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 6 }}>
                    <span className="text-mono" style={{ fontSize: 11, color: 'var(--text-muted)' }}>{tk.code}</span>
                    <span className="text-faint" style={{ fontSize: 10 }}>{_relTime(tk.updated_at)}</span>
                  </div>
                  <div style={{ fontSize: 12.5, fontWeight: 500, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{tk.subject || t.tx('(无主题)','(No subject)')}</div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4 }}>
                    <Avatar name={tk.user_name} size={16} />
                    <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>{tk.user_name}</span>
                  </div>
                  <div style={{ display: 'flex', gap: 4, marginTop: 5, flexWrap: 'wrap' }}>
                    <span className={'badge ' + _statusColor(tk.status)} style={{ fontSize: 9 }}>{tk.status}</span>
                    <span className={'badge ' + _priColor(tk.priority)} style={{ fontSize: 9 }}>{tk.priority}</span>
                    {tk.category && <span className="badge outline" style={{ fontSize: 9 }}>{tk.category}</span>}
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* middle: conversation */}
        <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
          <div className="card-h">
            <h3>{selected ? selected.subject : t.tx('对话','Conversation')}</h3>
            <span className="meta">{selected ? selected.code + ' · ' + selected.user_name : t.tx('未选','Not selected')}</span>
          </div>
          <div style={{ flex: 1, padding: 16, overflowY: 'auto', maxHeight: 600, background: 'var(--bg-inset)' }}>
            {!selected && <div style={{ fontSize: 12, color: 'var(--text-faint)' }}>{t.tx('从左侧选择一个工单查看对话','Select a ticket on the left to view conversation')}</div>}
            {selected && (!msgData || !msgData.messages) && <div style={{ fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('加载消息中…','Loading messages…')}</div>}
            {selected && msgData && (msgData.messages || []).length === 0 && <div style={{ fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('暂无对话','No messages')}</div>}
            {selected && msgData && (msgData.messages || []).map(m => {
              const isAdmin = m.sender_type === 'admin' || m.sender_type === 'system';
              return (
                <div key={m.id} style={{ display: 'flex', justifyContent: isAdmin ? 'flex-end' : 'flex-start', marginBottom: 10 }}>
                  <div style={{ maxWidth: '70%' }}>
                    <div style={{ fontSize: 10, color: 'var(--text-faint)', marginBottom: 3, textAlign: isAdmin ? 'right' : 'left' }}>
                      {m.sender_name || m.sender_type} · {_fmtTime(m.sent_at)}
                    </div>
                    <div style={{
                      padding: '8px 12px', borderRadius: 10, fontSize: 12.5, lineHeight: 1.5,
                      background: isAdmin ? 'var(--accent)' : 'var(--bg-card)',
                      color: isAdmin ? 'white' : 'var(--text)',
                      border: isAdmin ? 'none' : '1px solid var(--border)',
                      whiteSpace: 'pre-wrap', wordBreak: 'break-word',
                    }}>
                      {m.content}
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        {/* right: actions */}
        <div className="card">
          <div className="card-h"><h3>{t.tx('操作','Actions')}</h3></div>
          {!selected && <div style={{ padding: 16, fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('选择工单后可以接单 / 回复 / 解决 / 关闭','Select a ticket to assign / reply / resolve / close')}</div>}
          {selected && (
            <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 12 }}>
              <dl className="kv">
                <dt>{t.tx('工单号','Ticket #')}</dt><dd className="text-mono" style={{ fontSize: 11 }}>{selected.code}</dd>
                <dt>{t.tx('用户','User')}</dt><dd>{selected.user_name}</dd>
                <dt>{t.tx('类目','Category')}</dt><dd>{selected.category || '—'}</dd>
                <dt>{t.tx('状态','Status')}</dt><dd><span className={'badge ' + _statusColor(selected.status)} style={{ fontSize: 10 }}>{selected.status}</span></dd>
                <dt>{t.tx('优先级','Priority')}</dt><dd><span className={'badge ' + _priColor(selected.priority)} style={{ fontSize: 10 }}>{selected.priority}</span></dd>
                <dt>{t.tx('接单人','Assignee')}</dt><dd className="text-faint" style={{ fontSize: 11 }}>{selected.assigned_to || t.tx('未分配','Unassigned')}</dd>
                <dt>{t.tx('创建','Created')}</dt><dd className="text-faint text-mono" style={{ fontSize: 10 }}>{_fmtTime(selected.created_at)}</dd>
              </dl>

              <button className="btn btn-sm" disabled={submitting || !!selected.assigned_to} onClick={doAssign}>
                {selected.assigned_to ? t.tx('已接单','Assigned') : t.tx('接单','Assign')}
              </button>

              <div className="divider" />

              <div>
                <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('回复用户','Reply to User')}</div>
                <textarea className="textarea" rows={5} value={reply} onChange={e => setReply(e.target.value)} placeholder={t.tx('输入回复内容…','Enter reply…')} style={{ width: '100%', resize: 'vertical' }} />
                <button className="btn btn-pri btn-sm" style={{ width: '100%', marginTop: 6 }}
                        disabled={submitting || !reply.trim()} onClick={doReply}>
                  {submitting ? t.tx('发送中…','Sending…') : t.tx('发送回复','Send Reply')}
                </button>
              </div>

              <div className="divider" />

              <div style={{ display: 'flex', gap: 6 }}>
                <button className="btn btn-sm" style={{ flex: 1 }} disabled={submitting} onClick={doResolve}>{t.tx('已解决','Resolved')}</button>
                <button className="btn btn-sm btn-danger" style={{ flex: 1 }} disabled={submitting} onClick={doClose}>{t.tx('关闭工单','Close Ticket')}</button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function _StatCell({ label, value, color }) {
  return (
    <div>
      <div className="text-muted" style={{ fontSize: 11, marginBottom: 2 }}>{label}</div>
      <div className="tabular" style={{ fontSize: 18, fontWeight: 700, color: color || 'var(--text)' }}>{value}</div>
    </div>
  );
}

// ─── BroadcastScreen ────────────────────────────────────────────────────────
function BroadcastScreen({ t, push }) {
  // notif broadcast form
  const [kind, setKind] = React.useState('system');
  const [title, setTitle] = React.useState('');
  const [body, setBody] = React.useState('');
  const [link, setLink] = React.useState('');
  const [sendingNotif, setSendingNotif] = React.useState(false);

  // chat system broadcast
  const [chatMsg, setChatMsg] = React.useState('');
  const [sendingChat, setSendingChat] = React.useState(false);

  // rooms list + ban + delete
  const [rooms, setRooms] = React.useState([]);
  const [activeRoom, setActiveRoom] = React.useState('lobby');
  const [banUserId, setBanUserId] = React.useState('');
  const [banHours, setBanHours] = React.useState('24');
  const [banReason, setBanReason] = React.useState('');
  const [delMsgId, setDelMsgId] = React.useState('');

  React.useEffect(() => {
    _supFetch('/api/chat/rooms')
      .then(d => setRooms(d.rooms || []))
      .catch(e => push(t.tx('加载房间失败: ','Failed to load rooms: ') + (e.message || e)));
  }, []);

  const doSendNotif = async () => {
    if (!title.trim() || !body.trim()) { push(t.tx('标题和内容必填','Title and body required')); return; }
    setSendingNotif(true);
    try {
      await _supFetch('/api/admin/notif/broadcast', {
        method: 'POST',
        body: JSON.stringify({ kind, title: title.trim(), body: body.trim(), link: link.trim() || undefined }),
      });
      push(t.tx('广播已发送','Broadcast sent'));
      setTitle(''); setBody(''); setLink('');
    } catch (e) { push(t.tx('发送失败: ','Send failed: ') + (e.message || e)); }
    finally { setSendingNotif(false); }
  };

  const doSendChat = async () => {
    if (!chatMsg.trim()) { push(t.tx('内容必填','Content required')); return; }
    setSendingChat(true);
    try {
      await _supFetch('/api/chat/system/broadcast', { method: 'POST', body: JSON.stringify({ content: chatMsg.trim() }) });
      push(t.tx('已发送至 lobby system','Sent to lobby system'));
      setChatMsg('');
    } catch (e) { push(t.tx('发送失败: ','Send failed: ') + (e.message || e)); }
    finally { setSendingChat(false); }
  };

  const doBan = async () => {
    if (!banUserId.trim()) { push(t.tx('user_id 必填','user_id required')); return; }
    const hours = parseInt(banHours, 10) || 0;
    if (hours <= 0) { push(t.tx('时长必须 > 0','Duration must be > 0')); return; }
    try {
      await _supFetch(`/api/chat/${activeRoom}/ban`, {
        method: 'POST',
        body: JSON.stringify({ user_id: banUserId.trim(), hours, reason: banReason.trim() }),
      });
      push(`${t.tx('已禁言','Muted')} ${banUserId} ${hours}h`);
      setBanUserId(''); setBanReason('');
    } catch (e) { push(t.tx('禁言失败: ','Mute failed: ') + (e.message || e)); }
  };

  const doDel = async () => {
    if (!delMsgId.trim()) { push(t.tx('msg_id 必填','msg_id required')); return; }
    try {
      await _supFetch(`/api/chat/${activeRoom}/messages/${delMsgId.trim()}`, { method: 'DELETE' });
      push(t.tx('消息已软删','Message soft-deleted'));
      setDelMsgId('');
    } catch (e) { push(t.tx('删除失败: ','Delete failed: ') + (e.message || e)); }
  };

  const kindOptions = [
    { v: 'system',   label: t.tx('系统','System') },
    { v: 'promo',    label: t.tx('活动','Promo') },
    { v: 'win',      label: t.tx('中奖','Win') },
    { v: 'security', label: t.tx('安全','Security') },
  ];

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('消息广播 & 聊天管控','Broadcast & Chat Control')}</h1>
          <div className="sub">{t.tx('站内通知 · lobby system 消息 · 房间禁言 / 软删','In-app notif · lobby system messages · Room mute / soft-delete')}</div>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        {/* notif broadcast */}
        <div className="card">
          <div className="card-h"><h3>{t.tx('站内消息广播','In-App Broadcast')}</h3><span className="meta">{t.tx('推送给所有用户','Push to all users')}</span></div>
          <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 6 }}>{t.tx('类型','Type')}</div>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {kindOptions.map(o => (
                  <button key={o.v} className={'chip ' + (kind === o.v ? 'active' : '')} onClick={() => setKind(o.v)}>
                    {o.label}
                  </button>
                ))}
              </div>
            </div>
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('标题','Title')}</div>
              <input className="input" value={title} onChange={e => setTitle(e.target.value)} placeholder={t.tx('例：维护公告','e.g. Maintenance Notice')} />
            </div>
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('内容','Content')}</div>
              <textarea className="textarea" rows={4} value={body} onChange={e => setBody(e.target.value)} placeholder={t.tx('详细内容…','Details…')} style={{ width: '100%', resize: 'vertical' }} />
            </div>
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('链接（可选）','Link (optional)')}</div>
              <input className="input" value={link} onChange={e => setLink(e.target.value)} placeholder="/promo/xxx or https://..." />
            </div>
            <button className="btn btn-pri btn-sm" disabled={sendingNotif} onClick={doSendNotif}>
              {sendingNotif ? t.tx('发送中…','Sending…') : t.tx('发送广播','Send Broadcast')}
            </button>
            <div className="text-faint" style={{ fontSize: 10 }}>
              {t.tx('TODO: admin 视角查看历史广播列表 (GET /api/notif/me?user_id=*)','TODO: admin view of historical broadcasts (GET /api/notif/me?user_id=*)')}
            </div>
          </div>
        </div>

        {/* chat broadcast + control */}
        <div className="card">
          <div className="card-h"><h3>{t.tx('聊天广播 & 管控','Chat Broadcast & Control')}</h3><span className="meta">{t.tx('lobby system + 房间操作','lobby system + room ops')}</span></div>
          <div style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('lobby system 消息','lobby system message')}</div>
              <textarea className="textarea" rows={3} value={chatMsg} onChange={e => setChatMsg(e.target.value)} placeholder={t.tx('作为 system 角色发送到 lobby…','Send as system to lobby…')} style={{ width: '100%', resize: 'vertical' }} />
              <button className="btn btn-pri btn-sm" style={{ marginTop: 6 }} disabled={sendingChat} onClick={doSendChat}>
                {sendingChat ? t.tx('发送中…','Sending…') : t.tx('发送到 lobby','Send to lobby')}
              </button>
            </div>

            <div className="divider" />

            <div>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('聊天房间','Chat Room')}</div>
              <select className="select" value={activeRoom} onChange={e => setActiveRoom(e.target.value)} style={{ width: '100%' }}>
                {rooms.length === 0 && <option value="lobby">lobby</option>}
                {rooms.map(r => (
                  <option key={r.id} value={r.code || r.id}>
                    {r.name || r.code} · {r.type} · slow={r.slow_seconds}s
                  </option>
                ))}
              </select>
            </div>

            <div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: 10, background: 'var(--bg-inset)', borderRadius: 6 }}>
              <div className="text-muted" style={{ fontSize: 11, fontWeight: 600 }}>{t.tx('禁言用户','Mute User')}</div>
              <input className="input" placeholder="user_id" value={banUserId} onChange={e => setBanUserId(e.target.value)} />
              <div style={{ display: 'flex', gap: 6 }}>
                <input className="input" placeholder={t.tx('小时','Hours')} value={banHours} onChange={e => setBanHours(e.target.value)} style={{ width: 80 }} />
                <input className="input" placeholder={t.tx('原因','Reason')} value={banReason} onChange={e => setBanReason(e.target.value)} style={{ flex: 1 }} />
              </div>
              <button className="btn btn-sm btn-danger" onClick={doBan}>{t.tx('禁言','Mute')}</button>
            </div>

            <div style={{ display: 'flex', flexDirection: 'column', gap: 6, padding: 10, background: 'var(--bg-inset)', borderRadius: 6 }}>
              <div className="text-muted" style={{ fontSize: 11, fontWeight: 600 }}>{t.tx('软删消息','Soft-Delete Message')}</div>
              <input className="input" placeholder="msg_id" value={delMsgId} onChange={e => setDelMsgId(e.target.value)} />
              <button className="btn btn-sm btn-danger" onClick={doDel}>{t.tx('删除','Delete')}</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── _SupportLiveChat — split view ticket queue + chat panel + AI suggest ────
function _SupportLiveChat({ t, push }) {
  const [tick, setTick] = React.useState(0);
  const bump = () => setTick(v => v + 1);
  const [ticketsData] = _supPoll('/api/support/tickets?status=open&_=' + tick, 5000);
  const tickets = (ticketsData && ticketsData.tickets) || [];
  const [selectedId, setSelectedId] = React.useState(null);
  const [msgData, setMsgData] = React.useState(null);
  const [reply, setReply] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [suggestions, setSuggestions] = React.useState([]);
  const [suggestLoading, setSuggestLoading] = React.useState(false);
  const selected = tickets.find(x => x.id === selectedId) || null;

  React.useEffect(() => {
    if (!selectedId) { setMsgData(null); setSuggestions([]); return; }
    let cancelled = false;
    _supFetch(`/api/support/tickets/${selectedId}/messages`)
      .then(d => { if (!cancelled) setMsgData(d); })
      .catch(() => {});
    return () => { cancelled = true; };
  }, [selectedId, tick]);

  // Re-fetch messages on tick so live chat polling stays current.
  React.useEffect(() => {
    if (!selectedId) return;
    const i = setInterval(() => bump(), 3000);
    return () => clearInterval(i);
  }, [selectedId]);

  const fetchSuggest = async () => {
    if (!selected) return;
    setSuggestLoading(true);
    try {
      const d = await _supFetch(`/api/admin/support/tickets/${selected.id}/ai-suggest`, { method: 'POST', body: JSON.stringify({}) });
      setSuggestions(d.suggestions || []);
    } catch (e) {
      push(t.tx('AI 建议失败: ', 'AI suggest failed: ') + (e.message || e));
    } finally {
      setSuggestLoading(false);
    }
  };

  const doReply = async () => {
    if (!selected || !reply.trim()) return;
    setSubmitting(true);
    try {
      await _supFetch(`/api/support/tickets/${selected.id}/admin-reply`, { method: 'POST', body: JSON.stringify({ content: reply.trim() }) });
      setReply('');
      push(t.tx('回复已发送', 'Reply sent'));
      bump();
    } catch (e) { push(t.tx('回复失败: ', 'Reply failed: ') + (e.message || e)); }
    finally { setSubmitting(false); }
  };

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '300px 1fr', gap: 14, minHeight: 540 }}>
      <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
        <div className="card-h"><h3>{t.tx('实时队列', 'Live Queue')}</h3><span className="meta">{tickets.length}</span></div>
        <div style={{ overflowY: 'auto', flex: 1, maxHeight: 600 }}>
          {tickets.length === 0 && <div style={{ padding: 18, fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('当前无待处理工单', 'No pending tickets')}</div>}
          {tickets.map(tk => {
            const sel = tk.id === selectedId;
            return (
              <div key={tk.id} onClick={() => setSelectedId(tk.id)}
                   style={{ padding: '10px 12px', borderBottom: '1px solid var(--border)', cursor: 'pointer', background: sel ? 'var(--bg-inset)' : '' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 6 }}>
                  <span className="text-mono" style={{ fontSize: 11, color: 'var(--text-muted)' }}>{tk.code}</span>
                  <span className={'badge ' + _priColor(tk.priority)} style={{ fontSize: 9 }}>{tk.priority}</span>
                </div>
                <div style={{ fontSize: 12.5, fontWeight: 500, marginTop: 2, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{tk.subject || '(no subject)'}</div>
                <div style={{ fontSize: 10, color: 'var(--text-muted)', marginTop: 3 }}>{tk.user_name} · {_relTime(tk.updated_at)}</div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="card" style={{ display: 'flex', flexDirection: 'column' }}>
        <div className="card-h">
          <h3>{selected ? selected.subject : t.tx('实时对话', 'Live Conversation')}</h3>
          <span className="meta">{selected ? selected.code : t.tx('未选', 'Not selected')}</span>
        </div>
        <div style={{ flex: 1, padding: 14, overflowY: 'auto', maxHeight: 360, background: 'var(--bg-inset)' }}>
          {!selected && <div style={{ fontSize: 12, color: 'var(--text-faint)' }}>{t.tx('选择左侧工单开始对话', 'Select a ticket on the left to chat')}</div>}
          {selected && msgData && (msgData.messages || []).map(m => {
            const isAdmin = m.sender_type === 'admin' || m.sender_type === 'system';
            return (
              <div key={m.id} style={{ display: 'flex', justifyContent: isAdmin ? 'flex-end' : 'flex-start', marginBottom: 8 }}>
                <div style={{ maxWidth: '70%', padding: '8px 12px', borderRadius: 10, fontSize: 12.5, lineHeight: 1.5,
                              background: isAdmin ? 'var(--accent)' : 'var(--bg-card)',
                              color: isAdmin ? 'white' : 'var(--text)',
                              border: isAdmin ? 'none' : '1px solid var(--border)',
                              whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
                  <div style={{ fontSize: 10, opacity: 0.7, marginBottom: 3 }}>{m.sender_name || m.sender_type}</div>
                  {m.content}
                </div>
              </div>
            );
          })}
        </div>
        {selected && (
          <div style={{ padding: 12, borderTop: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: 8 }}>
            <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
              <button className="btn btn-sm" disabled={suggestLoading} onClick={fetchSuggest}>
                {suggestLoading ? t.tx('生成中…', 'Generating…') : t.tx('生成 AI 建议', 'Generate AI Suggest')}
              </button>
              {suggestions.length > 0 && suggestions.map((s, i) => (
                <button key={i} className="chip" onClick={() => setReply(s.reply)} title={s.intent}>
                  {t.tx('AI 建议回复 ', 'AI Reply ')}{i + 1} · {Math.round((s.confidence || 0) * 100)}%
                </button>
              ))}
            </div>
            <textarea className="textarea" rows={4} value={reply} onChange={e => setReply(e.target.value)}
                      placeholder={t.tx('输入回复或点击 AI 建议…', 'Enter reply or click an AI suggest…')}
                      style={{ width: '100%', resize: 'vertical' }} />
            <button className="btn btn-pri btn-sm" disabled={submitting || !reply.trim()} onClick={doReply}>
              {submitting ? t.tx('发送中…', 'Sending…') : t.tx('发送回复', 'Send Reply')}
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// ─── _SupportKB — knowledge base search + admin CRUD modal ──────────────────
function _SupportKB({ t, push }) {
  const [tick, setTick] = React.useState(0);
  const [q, setQ] = React.useState('');
  const [category, setCategory] = React.useState('');
  const [data, setData] = React.useState({ articles: [] });
  const [editing, setEditing] = React.useState(null);
  const [showModal, setShowModal] = React.useState(false);

  const load = React.useCallback(async () => {
    const p = new URLSearchParams();
    if (q) p.set('q', q);
    if (category) p.set('category', category);
    p.set('limit', '50');
    try {
      const d = await _supFetch('/api/kb/articles?' + p.toString());
      setData(d);
    } catch (e) { push(t.tx('加载失败: ', 'Load failed: ') + (e.message || e)); }
  }, [q, category, tick]);

  React.useEffect(() => { load(); }, [load]);

  const openCreate = () => { setEditing({ id: 0, category: 'payment', title: '', body: '', tags: [] }); setShowModal(true); };
  const openEdit = (a) => { setEditing({ ...a, tags: Array.isArray(a.tags) ? a.tags : [] }); setShowModal(true); };
  const save = async () => {
    if (!editing) return;
    const body = JSON.stringify({
      category: editing.category, title: editing.title, body: editing.body,
      tags: typeof editing.tagsRaw === 'string' ? editing.tagsRaw.split(',').map(s => s.trim()).filter(Boolean) : editing.tags,
    });
    try {
      if (editing.id) {
        await _supFetch('/api/admin/kb/articles/' + editing.id, { method: 'PUT', body });
        push(t.tx('已更新', 'Updated'));
      } else {
        await _supFetch('/api/admin/kb/articles', { method: 'POST', body });
        push(t.tx('已创建', 'Created'));
      }
      setShowModal(false); setEditing(null); setTick(v => v + 1);
    } catch (e) { push(t.tx('保存失败: ', 'Save failed: ') + (e.message || e)); }
  };
  const del = async (id) => {
    if (!confirm(t.tx('确认删除该文档？', 'Confirm delete this article?'))) return;
    try {
      await _supFetch('/api/admin/kb/articles/' + id, { method: 'DELETE' });
      push(t.tx('已删除', 'Deleted'));
      setTick(v => v + 1);
    } catch (e) { push(t.tx('删除失败: ', 'Delete failed: ') + (e.message || e)); }
  };

  const cats = ['', 'payment', 'account', 'vip', 'promo', 'other'];

  return (
    <div className="stack">
      <div className="card">
        <div className="card-h">
          <h3>{t.tx('知识库', 'Knowledge Base')}</h3>
          <span className="meta">{(data.articles || []).length} {t.tx('篇', 'articles')}</span>
          <div className="actions"><button className="btn btn-sm btn-pri" onClick={openCreate}>{t.tx('新建', 'New')}</button></div>
        </div>
        <div style={{ padding: 12, display: 'flex', gap: 8, flexWrap: 'wrap', borderBottom: '1px solid var(--border)' }}>
          <input className="input" placeholder={t.tx('搜索…', 'Search…')} value={q} onChange={e => setQ(e.target.value)} style={{ flex: 1, minWidth: 200 }} />
          <select className="select" value={category} onChange={e => setCategory(e.target.value)}>
            {cats.map(c => <option key={c} value={c}>{c || t.tx('全部', 'All')}</option>)}
          </select>
        </div>
        <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
          {(data.articles || []).length === 0 && <div style={{ fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('暂无匹配文档', 'No matching articles')}</div>}
          {(data.articles || []).map(a => (
            <div key={a.id} style={{ padding: 12, background: 'var(--bg-inset)', borderRadius: 8 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 8 }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 600 }}>{a.title}</div>
                  <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 4 }}>
                    <span className="badge outline">{a.category}</span> · {t.tx('浏览', 'Views')} {a.views} · 👍 {a.helpful} / 👎 {a.unhelpful}
                  </div>
                </div>
                <div style={{ display: 'flex', gap: 6 }}>
                  <button className="btn btn-sm" onClick={() => openEdit(a)}>{t.tx('编辑', 'Edit')}</button>
                  <button className="btn btn-sm btn-danger" onClick={() => del(a.id)}>{t.tx('删除', 'Delete')}</button>
                </div>
              </div>
              <div style={{ fontSize: 11, color: 'var(--text)', marginTop: 6, whiteSpace: 'pre-wrap', lineHeight: 1.5, opacity: 0.85 }}>
                {(a.body || '').slice(0, 240)}{(a.body || '').length > 240 ? '…' : ''}
              </div>
            </div>
          ))}
        </div>
      </div>

      {showModal && editing && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}
             onClick={() => setShowModal(false)}>
          <div className="card" style={{ width: 'min(600px, 92vw)', maxHeight: '85vh', overflowY: 'auto' }} onClick={e => e.stopPropagation()}>
            <div className="card-h">
              <h3>{editing.id ? t.tx('编辑文档', 'Edit Article') : t.tx('新建文档', 'New Article')}</h3>
            </div>
            <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
              <div>
                <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('分类', 'Category')}</div>
                <select className="select" value={editing.category} onChange={e => setEditing({ ...editing, category: e.target.value })} style={{ width: '100%' }}>
                  {cats.filter(c => c).map(c => <option key={c} value={c}>{c}</option>)}
                </select>
              </div>
              <div>
                <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('标题', 'Title')}</div>
                <input className="input" value={editing.title} onChange={e => setEditing({ ...editing, title: e.target.value })} />
              </div>
              <div>
                <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('正文', 'Body')}</div>
                <textarea className="textarea" rows={8} value={editing.body} onChange={e => setEditing({ ...editing, body: e.target.value })} style={{ width: '100%', resize: 'vertical' }} />
              </div>
              <div>
                <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{t.tx('标签 (逗号分隔)', 'Tags (comma-separated)')}</div>
                <input className="input" value={editing.tagsRaw != null ? editing.tagsRaw : (editing.tags || []).join(', ')}
                       onChange={e => setEditing({ ...editing, tagsRaw: e.target.value })} />
              </div>
              <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
                <button className="btn btn-sm" onClick={() => setShowModal(false)}>{t.tx('取消', 'Cancel')}</button>
                <button className="btn btn-sm btn-pri" onClick={save}>{t.tx('保存', 'Save')}</button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── _SupportDashboard — 大盘 metrics ────────────────────────────────────────
function _SupportDashboard({ t, push }) {
  const [data] = _supPoll('/api/admin/support/dashboard', 30000);
  const d = data || {};
  const cells = [
    { label: t.tx('活跃工单', 'Active Tickets'),       value: d.active_tickets ?? '—' },
    { label: t.tx('待分配', 'Queued'),                  value: d.queued ?? '—' },
    { label: t.tx('SLA 已违约', 'Breached SLA'),       value: d.breached_sla ?? '—', color: 'var(--danger)' },
    { label: t.tx('高优待办', 'High-Pri Pending'),     value: d.high_priority_pending ?? '—', color: 'var(--warning)' },
    { label: t.tx('今日新增', 'Total Today'),           value: d.total_today ?? '—' },
    { label: t.tx('今日已解决', 'Resolved Today'),      value: d.resolved_today ?? '—' },
    { label: t.tx('平均首响 (min)', 'Avg First Resp (min)'), value: d.avg_first_response_min ?? '—' },
    { label: t.tx('平均解决 (min)', 'Avg Resolve (min)'),    value: d.avg_resolve_min ?? '—' },
  ];
  return (
    <div className="stack">
      <div className="card">
        <div className="card-h">
          <h3>{t.tx('客服大盘', 'Support Dashboard')}</h3>
          <span className="meta">{t.tx('30s 缓存', '30s cache')}</span>
        </div>
        <div style={{ padding: 18, display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14 }}>
          {cells.map((c, i) => (
            <div key={i} style={{ padding: 14, background: 'var(--bg-inset)', borderRadius: 8 }}>
              <div className="text-muted" style={{ fontSize: 11, marginBottom: 6 }}>{c.label}</div>
              <div className="tabular" style={{ fontSize: 22, fontWeight: 700, color: c.color || 'var(--text)' }}>{c.value}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { SupportScreen, BroadcastScreen });
if (typeof window !== 'undefined') { window.SupportScreen = SupportScreen; window.BroadcastScreen = BroadcastScreen; }
