// screens-errors.jsx — frontend + backend error monitor.
// Reads /api/admin/errors and renders a paginated list with filters,
// expandable stack traces, and a "mark seen" action.

function _errSevColor(s) {
  if (s === 'error') return 'var(--danger)';
  if (s === 'warn')  return 'var(--warning)';
  return 'var(--text-muted)';
}

function _errFmtTime(iso) {
  if (!iso) return '—';
  const d = new Date(iso);
  if (isNaN(d)) return iso;
  const pad = (n) => String(n).padStart(2, '0');
  return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}

function _errTrunc(s, n) {
  s = String(s || '');
  return s.length > n ? s.slice(0, n) + '…' : s;
}

function ErrorsScreen({ t, push }) {
  const [severity, setSeverity] = React.useState('');     // '', 'error', 'warn', 'info'
  const [unseenOnly, setUnseenOnly] = React.useState(true);
  const [tick, setTick] = React.useState(0);
  const [expanded, setExpanded] = React.useState(null);   // row id

  const query = React.useMemo(() => {
    const p = new URLSearchParams();
    p.set('limit', '100');
    if (severity) p.set('severity', severity);
    if (unseenOnly) p.set('seen', '0');
    return '/api/admin/errors?' + p.toString() + '&_t=' + tick;
  }, [severity, unseenOnly, tick]);

  const [data, err] = NebulaAdmin.useAdminPoll(query, 10000);
  const rows = (data && data.events) || [];

  const markSeen = async (id) => {
    try {
      await NebulaAdmin.adminFetch(`/api/admin/errors/${id}/seen`, { method: 'POST' });
      push(t.tx('已标记为已读', 'Marked as seen'));
      setTick(x => x + 1);
    } catch (e) {
      push(t.tx('标记失败：', 'Mark failed: ') + (e.message || e));
    }
  };

  const markAllSeen = async () => {
    const ids = rows.filter(r => r.seen === 0).map(r => r.id);
    if (ids.length === 0) return;
    if (!confirm(t.tx(`将 ${ids.length} 条标记为已读？`, `Mark ${ids.length} entries as seen?`))) return;
    await Promise.all(ids.map(id =>
      NebulaAdmin.adminFetch(`/api/admin/errors/${id}/seen`, { method: 'POST' }).catch(() => {})
    ));
    push(t.tx('全部已读', 'All marked seen'));
    setTick(x => x + 1);
  };

  const unseenCount = rows.filter(r => r.seen === 0).length;

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('错误监控','Error Monitor')}</h1>
          <div className="sub">
            {t.tx('前端 window.onerror + 后端异常 · 共','Frontend window.onerror + backend exceptions · Total')} {rows.length}
            {' · '}{t.tx('未读','Unseen')} {unseenCount}
          </div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={() => setTick(x => x + 1)}>
            <Icons.refresh /> {t.tx('刷新','Refresh')}
          </button>
          <button className="btn btn-sm btn-pri" onClick={markAllSeen} disabled={unseenCount === 0}>
            {t.tx('全部标为已读','Mark all seen')}
          </button>
        </div>
      </div>

      <div className="card">
        <div className="filter-bar">
          <select className="input" value={severity} onChange={(e) => setSeverity(e.target.value)} style={{ width: 140, height: 26, fontSize: 12 }}>
            <option value="">{t.tx('全部级别','All severities')}</option>
            <option value="error">error</option>
            <option value="warn">warn</option>
            <option value="info">info</option>
          </select>
          <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 12, cursor: 'pointer' }}>
            <input type="checkbox" checked={unseenOnly} onChange={(e) => setUnseenOnly(e.target.checked)} />
            {t.tx('仅未读','Only unseen')}
          </label>
          <div style={{ flex: 1 }} />
          {err && <span className="text-faint" style={{ fontSize: 11, color: 'var(--danger)' }}>
            {t.tx('加载失败：','Load failed: ')}{String(err.message || err)}
          </span>}
        </div>
      </div>

      <div className="card">
        <div className="tbl-scroll">
        <table className="tbl tbl-stack">
          <thead>
            <tr>
              <th style={{ width: 150 }}>{t.tx('时间','Time')}</th>
              <th style={{ width: 70 }}>{t.tx('级别','Severity')}</th>
              <th style={{ width: 80 }}>{t.tx('来源','Source')}</th>
              <th>{t.tx('消息','Message')}</th>
              <th style={{ width: 200 }}>URL</th>
              <th style={{ width: 100 }}>user_id</th>
              <th style={{ width: 70 }}>{t.tx('状态','State')}</th>
              <th style={{ width: 90 }}>{t.tx('操作','Actions')}</th>
            </tr>
          </thead>
          <tbody>
            {rows.length === 0 ? (
              <tr><td colSpan={8} className="muted" style={{ textAlign: 'center', padding: 24 }}>
                {t.tx('暂无错误记录','No error records')}
              </td></tr>
            ) : rows.map((e) => {
              const isOpen = expanded === e.id;
              return (
                <React.Fragment key={e.id}>
                  <tr style={{ cursor: 'pointer', background: isOpen ? 'var(--bg-active)' : undefined }}
                      onClick={() => setExpanded(isOpen ? null : e.id)}>
                    <td data-label={t.tx('时间','Time')} className="text-mono muted" style={{ fontSize: 11 }}>{_errFmtTime(e.occurred_at)}</td>
                    <td data-label={t.tx('级别','Severity')}>
                      <span className="badge" style={{ fontSize: 10, color: _errSevColor(e.severity), borderColor: _errSevColor(e.severity) }}>
                        {e.severity}
                      </span>
                    </td>
                    <td data-label={t.tx('来源','Source')}><span className="badge outline" style={{ fontSize: 10 }}>{e.source}</span></td>
                    <td data-label={t.tx('消息','Message')} className="text-mono" style={{ fontSize: 11, maxWidth: 480, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {_errTrunc(e.message, 160)}
                    </td>
                    <td data-label="URL" className="text-mono muted" style={{ fontSize: 10, maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                      {_errTrunc(e.url, 60)}
                    </td>
                    <td data-label="user_id" className="text-mono" style={{ fontSize: 11 }}>{e.user_id || '—'}</td>
                    <td data-label={t.tx('状态','State')}>
                      <span className={'badge ' + (e.seen ? 'success' : 'danger')} style={{ fontSize: 10 }}>
                        {e.seen ? t.tx('已读','Seen') : t.tx('未读','New')}
                      </span>
                    </td>
                    <td data-label={t.tx('操作','Actions')}>
                      {e.seen === 0 && (
                        <button className="btn btn-sm" onClick={(ev) => { ev.stopPropagation(); markSeen(e.id); }} style={{ fontSize: 10, padding: '2px 8px' }}>
                          {t.tx('标为已读','Mark seen')}
                        </button>
                      )}
                    </td>
                  </tr>
                  {isOpen && (
                    <tr>
                      <td colSpan={8} style={{ background: 'var(--bg-inset)', padding: 12 }}>
                        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, fontSize: 11 }}>
                          <div>
                            <div className="text-muted" style={{ marginBottom: 4 }}>{t.tx('完整消息','Full message')}</div>
                            <pre style={{ background: 'var(--bg-card)', border: '1px solid var(--border)', borderRadius: 6, padding: 8, margin: 0, fontSize: 11, whiteSpace: 'pre-wrap', wordBreak: 'break-all', maxHeight: 160, overflow: 'auto' }}>
                              {e.message}
                            </pre>
                            <div className="text-muted" style={{ marginTop: 8, marginBottom: 4 }}>URL</div>
                            <div className="text-mono" style={{ wordBreak: 'break-all' }}>{e.url || '—'}</div>
                            <div className="text-muted" style={{ marginTop: 8, marginBottom: 4 }}>user_agent</div>
                            <div className="text-mono text-faint" style={{ fontSize: 10, wordBreak: 'break-all' }}>{e.user_agent || '—'}</div>
                            <div className="text-muted" style={{ marginTop: 8, marginBottom: 4 }}>request_id</div>
                            <div className="text-mono">{e.request_id || '—'}</div>
                          </div>
                          <div>
                            <div className="text-muted" style={{ marginBottom: 4 }}>{t.tx('堆栈','Stack')}</div>
                            <pre style={{ background: 'var(--bg-card)', border: '1px solid var(--border)', borderRadius: 6, padding: 8, margin: 0, fontSize: 10, fontFamily: 'var(--font-mono)', whiteSpace: 'pre', overflow: 'auto', maxHeight: 320 }}>
                              {e.stack || t.tx('(无堆栈)','(no stack)')}
                            </pre>
                          </div>
                        </div>
                      </td>
                    </tr>
                  )}
                </React.Fragment>
              );
            })}
          </tbody>
        </table>
        </div>
      </div>
    </div>
  );
}

window.ErrorsScreen = ErrorsScreen;
