// screens-content-ops.jsx — Content review + on/off-shelf ops, wired to backend
// Exports: VideosScreen, LiveScreen, GamesScreen, CMSScreen, BrandMatrixScreen
// Backend at :5174. Uses window.NebulaAdmin helpers.

const { adminFetch, useAdminPoll } = window.NebulaAdmin;

// ── tiny local hook: bump a key to force useAdminPoll re-mount + refetch ──
function useRefetchKey() {
  const [k, setK] = React.useState(0);
  return [k, () => setK((x) => x + 1)];
}

function pollKey(path, key) {
  // append a cache-bust query to invalidate the hook's effect dependency
  if (!path) return path;
  return path + (path.indexOf('?') >= 0 ? '&' : '?') + '_r=' + key;
}

// ════════════════════════════════════════════════════════════════════════════
// VIDEOS · 短视频审核
// ════════════════════════════════════════════════════════════════════════════
function VideosScreen({ t, push }) {
  const [tab, setTab] = React.useState('pending'); // pending / feed / banned
  const [rkey, refetch] = useRefetchKey();

  const path = tab === 'pending'
    ? '/api/admin/video/pending'
    : '/api/video/feed?limit=50';
  const [data, err] = useAdminPoll(pollKey(path, rkey), 8000, true);

  const allVideos = (data && data.videos) || [];
  const videos = tab === 'banned'
    ? allVideos.filter(v => v.status === 'banned')
    : tab === 'feed'
      ? allVideos.filter(v => v.status !== 'banned')
      : allVideos;

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('短视频审核', 'Video Review')}</h1>
          <div className="sub">{t.tx('人工审核 + 上下架 · 实时拉取后端审核队列', 'Manual review + publish · live backend queue')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={refetch}><Icons.refresh /> {t.tx('刷新', 'Refresh')}</button>
        </div>
      </div>

      <div className="tabs">
        {[['pending', t.tx('待审', 'Pending')],['feed', t.tx('已上架', 'Published')],['banned', t.tx('已封禁', 'Banned')]].map(([k,l]) => (
          <div key={k} className={'tab' + (tab === k ? ' active' : '')} onClick={() => setTab(k)}>{l}</div>
        ))}
      </div>

      {err && <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败：', 'Load failed: ')}{String(err.message || err)}</div>}
      {!data && !err && <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}
      {data && videos.length === 0 && <div className="empty">{t.tx('暂无视频', 'No videos')}</div>}

      {data && videos.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(260px, 1fr))', gap: 14 }}>
          {videos.map((v, i) => (
            <VideoCard key={v.code} v={v} idx={i} push={push} onAction={refetch} reviewMode={tab === 'pending'} t={t} />
          ))}
        </div>
      )}
    </div>
  );
}

function VideoCard({ v, idx, push, onAction, reviewMode, t }) {
  const [expanded, setExpanded] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [banReason, setBanReason] = React.useState('');
  const [showBanInput, setShowBanInput] = React.useState(false);

  const approve = async () => {
    setBusy(true);
    try {
      await adminFetch('/api/admin/video/' + v.code + '/approve', { method: 'PUT' });
      push(t.tx('已通过 ', 'Approved ') + v.code);
      onAction();
    } catch (e) { push(t.tx('通过失败：', 'Approve failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const ban = async () => {
    if (!banReason.trim()) { setShowBanInput(true); return; }
    setBusy(true);
    try {
      await adminFetch('/api/admin/video/' + v.code + '/ban', {
        method: 'PUT',
        body: JSON.stringify({ reason: banReason.trim() }),
      });
      push(t.tx('已封禁 ', 'Banned ') + v.code);
      setShowBanInput(false);
      setBanReason('');
      onAction();
    } catch (e) { push(t.tx('封禁失败：', 'Ban failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };

  const statusBadge =
    v.status === 'banned'    ? <span className="badge danger">{t.tx('已封', 'Banned')}</span>
    : v.status === 'pending' ? <span className="badge warning">{t.tx('待审', 'Pending')}</span>
    : v.status === 'live'    ? <span className="badge success">{t.tx('已上架', 'Live')}</span>
    : <span className="badge outline">{v.status}</span>;

  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div style={{
        aspectRatio: '9/14',
        background: `linear-gradient(135deg, oklch(0.5 0.12 ${(idx * 47) % 360}), oklch(0.3 0.18 ${(idx * 71) % 360}))`,
        position: 'relative', cursor: 'pointer'
      }} onClick={() => setExpanded(e => !e)}>
        <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0) 30%, rgba(0,0,0,0.8))' }} />
        <div style={{ position: 'absolute', top: 8, left: 8, right: 8, display: 'flex', justifyContent: 'space-between' }}>
          {statusBadge}
          <span className="text-mono" style={{ fontSize: 9, color: 'white', background: 'rgba(0,0,0,0.5)', padding: '1px 5px', borderRadius: 3 }}>{v.code}</span>
        </div>
        <div style={{ position: 'absolute', bottom: 8, left: 8, right: 8, color: 'white' }}>
          <div style={{ fontSize: 12, fontWeight: 600, lineHeight: 1.3, textShadow: '0 1px 3px rgba(0,0,0,0.6)', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', marginBottom: 4 }}>
            {v.title || v.caption || t.tx('(无标题)', '(untitled)')}
          </div>
          <div style={{ fontSize: 10, opacity: 0.9, display: 'flex', justifyContent: 'space-between' }}>
            <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '60%' }}>@{v.author_name || t.tx('匿名', 'anon')}</span>
            <span>▶ {fmtNum(v.plays || 0)}</span>
          </div>
        </div>
      </div>
      <div style={{ padding: 10, fontSize: 11, color: 'var(--text-muted)', display: 'flex', gap: 10, alignItems: 'center' }}>
        <span>♥ {fmtNum(v.likes || 0)}</span>
        <span>💬 {fmtNum(v.comments || 0)}</span>
        <span style={{ marginLeft: 'auto' }} className="text-mono text-faint">{t.tx('热 ', 'Hot ')}{Math.round(v.hot_score || 0)}</span>
      </div>

      {reviewMode && (
        <div style={{ padding: '0 10px 10px', display: 'flex', gap: 6 }}>
          <button className="btn btn-xs btn-pri" disabled={busy} onClick={approve} style={{ flex: 1 }}>{t.tx('通过', 'Approve')}</button>
          <button className="btn btn-xs btn-danger" disabled={busy} onClick={() => setShowBanInput(s => !s)} style={{ flex: 1 }}>{t.tx('禁', 'Ban')}</button>
        </div>
      )}
      {!reviewMode && v.status !== 'banned' && (
        <div style={{ padding: '0 10px 10px' }}>
          <button className="btn btn-xs btn-danger" disabled={busy} onClick={() => setShowBanInput(s => !s)} style={{ width: '100%' }}>{t.tx('下架/封禁', 'Unpublish / Ban')}</button>
        </div>
      )}
      {showBanInput && (
        <div style={{ padding: '0 10px 10px', display: 'flex', gap: 4 }}>
          <input className="input" placeholder={t.tx('封禁原因…', 'Ban reason…')} value={banReason} onChange={(e) => setBanReason(e.target.value)} style={{ fontSize: 11, flex: 1 }} />
          <button className="btn btn-xs btn-danger" disabled={busy || !banReason.trim()} onClick={ban}>{t.tx('确认', 'Confirm')}</button>
        </div>
      )}

      <div style={{ padding: '8px 10px', borderTop: '1px solid var(--border)', fontSize: 11, color: 'var(--text-muted)', cursor: 'pointer' }}
           onClick={() => setExpanded(e => !e)}>
        {expanded ? t.tx('▼ 收起评论', '▼ Hide comments') : t.tx('▶ 查看评论', '▶ View comments')}
      </div>
      {expanded && <CommentsList code={v.code} push={push} t={t} />}
    </div>
  );
}

function CommentsList({ code, push, t }) {
  const [comments, setComments] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const load = React.useCallback(async () => {
    try {
      const j = await adminFetch('/api/video/' + code + '/comments?limit=20');
      setComments(j.comments || []);
    } catch (e) { setErr(e); }
  }, [code]);
  React.useEffect(() => { load(); }, [load]);

  const del = async (id) => {
    try {
      await adminFetch('/api/video/comments/' + id, { method: 'DELETE' });
      push(t.tx('已删评论 #', 'Deleted comment #') + id);
      load();
    } catch (e) { push(t.tx('删除失败：', 'Delete failed: ') + (e.body?.error || e.message)); }
  };

  if (err) return <div style={{ padding: 10, fontSize: 11, color: 'var(--danger)' }}>{t.tx('评论加载失败', 'Failed to load comments')}</div>;
  if (!comments) return <div style={{ padding: 10, fontSize: 11, color: 'var(--text-muted)' }}>{t.tx('加载评论中…', 'Loading comments…')}</div>;
  if (comments.length === 0) return <div style={{ padding: 10, fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('暂无评论', 'No comments')}</div>;

  return (
    <div style={{ padding: '4px 0', maxHeight: 240, overflowY: 'auto', background: 'var(--bg-inset)' }}>
      {comments.map((c) => (
        <div key={c.id} style={{ padding: '6px 10px', display: 'flex', gap: 6, alignItems: 'flex-start', borderBottom: '1px solid var(--border)' }}>
          <div style={{ flex: 1, fontSize: 11 }}>
            <div style={{ display: 'flex', gap: 6, alignItems: 'center', marginBottom: 2 }}>
              <span style={{ fontWeight: 500 }}>{c.user_name || c.user || t.tx('匿名', 'anon')}</span>
              <span className="text-faint" style={{ fontSize: 10 }}>#{c.id}</span>
            </div>
            <div style={{ lineHeight: 1.4 }}>{c.content || c.text || ''}</div>
          </div>
          <button className="btn btn-xs btn-ghost" onClick={() => del(c.id)} title={t.tx('删除', 'Delete')}>
            <Icons.trash />
          </button>
        </div>
      ))}
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// LIVE · 直播间监控
// ════════════════════════════════════════════════════════════════════════════
function LiveScreen({ t, push }) {
  const [statusFilter, setStatusFilter] = React.useState(''); // '' = all
  const [rkey, refetch] = useRefetchKey();
  const path = '/api/admin/live/rooms' + (statusFilter ? '?status=' + statusFilter : '');
  const [data, err] = useAdminPoll(pollKey(path, rkey), 5000, true);
  const rooms = (data && data.rooms) || [];

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('直播间监控', 'Live Room Monitor')}</h1>
          <div className="sub">{t.tx('实时监控所有开播 / 已下播房间 · 5s 自动刷新', 'Realtime monitor of live / ended rooms · 5s auto refresh')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={refetch}><Icons.refresh /> {t.tx('刷新', 'Refresh')}</button>
        </div>
      </div>

      <div className="card">
        <div className="filter-bar">
          {[['', t.tx('全部', 'All')], ['live', t.tx('在播', 'Live')], ['ended', t.tx('已结束', 'Ended')], ['banned', t.tx('已封禁', 'Banned')]].map(([k, l]) => (
            <span key={k || 'all'} className={'chip ' + (statusFilter === k ? 'active' : '')} onClick={() => setStatusFilter(k)}>{l}</span>
          ))}
          <span style={{ marginLeft: 'auto', fontSize: 11 }} className="text-muted">{rooms.length}{t.tx(' 间', ' rooms')}</span>
        </div>

        {err && <div style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败：', 'Load failed: ')}{String(err.message || err)}</div>}
        {!data && !err && <div style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}
        {data && rooms.length === 0 && <div className="empty">{t.tx('没有匹配的直播间', 'No matching rooms')}</div>}
        {data && rooms.length > 0 && (
          <div className="tbl-scroll">
          <table className="tbl tbl-stack">
            <thead><tr>
              <th>{t.tx('房间', 'Room')}</th><th>{t.tx('主播', 'Anchor')}</th><th>{t.tx('类目', 'Category')}</th>
              <th className="right">{t.tx('在线', 'Online')}</th><th className="right">{t.tx('峰值', 'Peak')}</th><th className="right">{t.tx('累计礼物', 'Gifts')}</th>
              <th>{t.tx('状态', 'Status')}</th><th></th>
            </tr></thead>
            <tbody>
              {rooms.map((r) => (
                <LiveRoomRow key={r.code} r={r} push={push} onAction={refetch} t={t} />
              ))}
            </tbody>
          </table>
          </div>
        )}
      </div>
    </div>
  );
}

function LiveRoomRow({ r, push, onAction, t }) {
  const [busy, setBusy] = React.useState(false);
  const [showInput, setShowInput] = React.useState(false);
  const [reason, setReason] = React.useState('');

  const doBan = async () => {
    if (!reason.trim()) return;
    setBusy(true);
    try {
      await adminFetch('/api/admin/live/rooms/' + r.code + '/ban', {
        method: 'POST',
        body: JSON.stringify({ reason: reason.trim() }),
      });
      push(t.tx('已强制下播 ', 'Force-ended ') + r.code);
      setShowInput(false);
      setReason('');
      onAction();
    } catch (e) { push(t.tx('操作失败：', 'Action failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };

  const statusBadge =
    r.status === 'live'   ? <span className="badge danger"><span className="dot" />LIVE</span>
    : r.status === 'banned' ? <span className="badge danger">{t.tx('已封', 'Banned')}</span>
    : r.status === 'ended'  ? <span className="badge outline">{t.tx('已结束', 'Ended')}</span>
    : <span className="badge outline">{r.status}</span>;

  return (
    <>
      <tr>
        <td data-label={t.tx('房间', 'Room')}>
          <div style={{ fontWeight: 500 }}>{r.title || t.tx('(无标题)', '(untitled)')}</div>
          <div className="text-mono text-faint" style={{ fontSize: 10 }}>{r.code}</div>
        </td>
        <td data-label={t.tx('主播', 'Anchor')}>
          <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
            <Avatar name={r.host_name || '?'} size={22} />
            <span>{r.host_name || '—'}</span>
          </div>
        </td>
        <td data-label={t.tx('类目', 'Category')}><span className="badge outline">{r.category || '—'}</span></td>
        <td data-label={t.tx('在线', 'Online')} className="right tabular">{fmtNum(r.viewer_count || 0)}</td>
        <td data-label={t.tx('峰值', 'Peak')} className="right tabular muted">{fmtNum(r.peak_viewers || 0)}</td>
        <td data-label={t.tx('累计礼物', 'Gifts')} className="right tabular">¥{fmtNum(Math.round(r.total_gifts_value || 0))}</td>
        <td data-label={t.tx('状态', 'Status')}>{statusBadge}</td>
        <td data-label={t.tx('操作', 'Actions')} className="tbl-actions">
          {r.status === 'live' && (
            <button className="btn btn-xs btn-danger" disabled={busy} onClick={() => setShowInput(s => !s)}>{t.tx('强制下播', 'Force end')}</button>
          )}
        </td>
      </tr>
      {showInput && (
        <tr>
          <td colSpan={8} style={{ background: 'var(--bg-inset)' }}>
            <div style={{ display: 'flex', gap: 6, alignItems: 'center', padding: '4px 0' }}>
              <span className="text-muted" style={{ fontSize: 11 }}>{t.tx('下播原因：', 'Reason:')}</span>
              <input className="input" placeholder={t.tx('如：违规直播内容', 'e.g. policy violation')} value={reason} onChange={(e) => setReason(e.target.value)} style={{ fontSize: 12, flex: 1, maxWidth: 360 }} autoFocus />
              <button className="btn btn-xs btn-danger" disabled={busy || !reason.trim()} onClick={doBan}>{t.tx('确认下播', 'Confirm end')}</button>
              <button className="btn btn-xs btn-ghost" onClick={() => { setShowInput(false); setReason(''); }}>{t.tx('取消', 'Cancel')}</button>
            </div>
          </td>
        </tr>
      )}
    </>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// GAMES · 游戏管理 + RTP 监控 + 报表
// ════════════════════════════════════════════════════════════════════════════
function GamesScreen({ t, push }) {
  const [view, setView] = React.useState('manage'); // 'manage' | 'reports'
  return (
    <div className="stack">
      <div className="tabs" style={{ marginBottom: 0 }}>
        <div className={'tab' + (view === 'manage' ? ' active' : '')} onClick={() => setView('manage')}>{t.tx('管理', 'Manage')}</div>
        <div className={'tab' + (view === 'reports' ? ' active' : '')} onClick={() => setView('reports')}>{t.tx('报表', 'Reports')}</div>
      </div>
      {view === 'manage' ? <GamesManage t={t} push={push} /> : <GamesReports t={t} push={push} />}
    </div>
  );
}

function GamesManage({ t, push }) {
  const [rkey, refetch] = useRefetchKey();
  const [tab, setTab] = React.useState('all'); // 'all' | 'pgsoft' | 'jili' | 'evo' | 'internal'
  const gamesUrl = tab === 'all' ? '/api/admin/games' : '/api/admin/games?provider=' + tab;
  const [games, gamesErr] = useAdminPoll(pollKey(gamesUrl, rkey), 10000, true);
  const [stats, statsErr] = useAdminPoll(pollKey('/api/admin/games/stats', rkey), 10000, true);
  const [providers, providersErr] = useAdminPoll(pollKey('/api/admin/providers', rkey), 10000, true);
  const [showAdd, setShowAdd] = React.useState(false);

  const list = (games && games.games) || [];
  const provList = (providers && providers.providers) || [];
  const statsMap = {};
  if (stats && stats.stats) stats.stats.forEach(s => { statsMap[s.game_id] = s; });

  const TABS = [
    ['all',      t.tx('所有',     'All')],
    ['pgsoft',   t.tx('PG Soft',  'PG Soft')],
    ['jili',     t.tx('JILI',     'JILI')],
    ['evo',      t.tx('Evolution','Evolution')],
    ['internal', t.tx('内置',     'Internal')],
  ];

  const syncCatalog = async (pid) => {
    try {
      const r = await adminFetch('/api/admin/games/sync/' + pid, { method: 'POST' });
      push(t.tx('已同步 ', 'Synced ') + pid + ' · +' + (r.inserted || 0));
      refetch();
    } catch (e) { push(t.tx('同步失败：', 'Sync failed: ') + (e.body?.error || e.message)); }
  };

  const healthPing = async (pid) => {
    try {
      await adminFetch('/api/admin/providers/' + pid + '/health-ping', { method: 'POST' });
      push(t.tx('心跳已发送 ', 'Ping sent ') + pid);
      refetch();
    } catch (e) { push(t.tx('心跳失败：', 'Ping failed: ') + (e.body?.error || e.message)); }
  };

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('游戏管理', 'Games')}</h1>
          <div className="sub">{t.tx('多厂商接入 · 实时 RTP / GGR · 健康脉冲', 'Multi-provider · realtime RTP / GGR · health pulse')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={refetch}><Icons.refresh /> {t.tx('刷新', 'Refresh')}</button>
          {tab !== 'all' && (
            <button className="btn btn-sm" onClick={() => syncCatalog(tab)}>{t.tx('同步目录', 'Sync Catalog')}</button>
          )}
          <button className="btn btn-pri btn-sm" onClick={() => setShowAdd(true)}><Icons.plus /> {t.tx('添加游戏', 'Add Game')}</button>
        </div>
      </div>

      <div className="tabs">
        {TABS.map(([k, l]) => (
          <div key={k} className={'tab' + (tab === k ? ' active' : '')} onClick={() => setTab(k)}>{l}</div>
        ))}
      </div>

      {/* Provider summary cards */}
      {providersErr && <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('厂商列表加载失败', 'Failed to load providers')}</div>}
      {provList.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(220px, 1fr))', gap: 12 }}>
          {provList
            .filter(p => tab === 'all' || p.id === tab)
            .map(p => <ProviderCard key={p.id} p={p} t={t} onPing={() => healthPing(p.id)} onSync={() => syncCatalog(p.id)} />)}
        </div>
      )}

      {gamesErr && <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('游戏列表加载失败', 'Failed to load games')}</div>}
      {!games && !gamesErr && <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}

      {games && (
        <div className="card">
          <div className="tbl-scroll">
          <table className="tbl tbl-stack">
            <thead><tr>
              <th>{t.tx('游戏', 'Game')}</th><th>{t.tx('厂商', 'Provider')}</th><th>{t.tx('分类', 'Category')}</th><th>{t.tx('RTP 配置', 'RTP Config')}</th><th>{t.tx('RTP 实际', 'RTP Actual')}</th>
              <th className="right">{t.tx('流水', 'Stake')}</th><th className="right">{t.tx('玩家', 'Players')}</th><th>{t.tx('状态', 'Status')}</th><th></th>
            </tr></thead>
            <tbody>
              {list.length === 0 && <tr><td colSpan={9}><div className="empty">{tab === 'all' ? t.tx('还没有接入游戏 · 点击右上「添加游戏」', 'No games yet · click "Add Game" at top right') : t.tx('此厂商下还没有游戏 · 点击「同步目录」', 'No games for this provider · click "Sync Catalog"')}</div></td></tr>}
              {list.map(g => (
                <GameRow key={g.id} g={g} stat={statsMap[g.id]} push={push} onAction={refetch} t={t} />
              ))}
            </tbody>
          </table>
          </div>
          {statsErr && <div style={{ padding: 10, fontSize: 11, color: 'var(--text-faint)' }}>{t.tx('(RTP 统计暂时不可用)', '(RTP stats unavailable)')}</div>}
        </div>
      )}

      {showAdd && <AddGameModal onClose={() => setShowAdd(false)} push={push} onCreated={() => { setShowAdd(false); refetch(); }} t={t} />}
    </div>
  );
}

function ProviderCard({ p, t, onPing, onSync }) {
  const today = p.today || {};
  const health = p.health || {};
  // Health pulse: green if last_callback < 5min, yellow < 1h, red > 1h, gray = unknown.
  let pulseColor = 'var(--text-faint)';
  let pulseLabel = t.tx('未知', 'Unknown');
  const last = health.last_callback_at;
  if (last) {
    const delta = (Date.now() - new Date(last).getTime()) / 1000;
    if (delta < 300) { pulseColor = 'var(--success)'; pulseLabel = t.tx('正常', 'OK'); }
    else if (delta < 3600) { pulseColor = 'oklch(0.65 0.16 60)'; pulseLabel = t.tx('降级', 'Degraded'); }
    else { pulseColor = 'var(--danger)'; pulseLabel = t.tx('离线', 'Down'); }
  }
  return (
    <div className="card" style={{ padding: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
        <span style={{ width: 8, height: 8, borderRadius: '50%', background: pulseColor,
          boxShadow: '0 0 6px ' + pulseColor, display: 'inline-block' }} />
        <span style={{ fontWeight: 600 }}>{p.name}</span>
        <span className="badge outline" style={{ marginLeft: 'auto', fontSize: 10 }}>{p.auth_kind}</span>
      </div>
      <div style={{ fontSize: 11, color: 'var(--text-muted)', marginBottom: 8 }}>
        {t.tx('健康：', 'Health: ')}<span style={{ color: pulseColor, fontWeight: 600 }}>{pulseLabel}</span>
        {' · '}{p.active ? t.tx('启用', 'Active') : t.tx('已暂停', 'Paused')}
      </div>
      <div className="text-mono" style={{ fontSize: 11, lineHeight: 1.6 }}>
        <div>{t.tx('今日下注', 'Today bets')}: <b>¥{fmtNum(Math.round(today.bet_amount || 0))}</b> ({today.bet_count || 0})</div>
        <div>{t.tx('今日派彩', 'Today wins')}: <b>¥{fmtNum(Math.round(today.win_amount || 0))}</b></div>
        <div>{t.tx('GGR', 'GGR')}: <b style={{ color: (today.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(today.ggr || 0))}</b></div>
        <div>{t.tx('RTP', 'RTP')}: <b>{((today.rtp || 0) * 100).toFixed(2)}%</b></div>
      </div>
      <div style={{ display: 'flex', gap: 6, marginTop: 10 }}>
        <button className="btn btn-xs" onClick={onPing}>{t.tx('心跳', 'Ping')}</button>
        <button className="btn btn-xs" onClick={onSync}>{t.tx('同步目录', 'Sync')}</button>
      </div>
    </div>
  );
}

function GameRow({ g, stat, push, onAction, t }) {
  const [busy, setBusy] = React.useState(false);
  const cfgRtp = Number(g.rtp) || 0;
  const realRtp = stat ? (Number(stat.actual_rtp || stat.real_rtp) || 0) : null;
  const deviationPct = (realRtp != null && cfgRtp > 0) ? Math.abs((realRtp - cfgRtp) / cfgRtp) * 100 : null;
  let rtpColor = 'var(--text-muted)';
  if (deviationPct != null) {
    if (deviationPct <= 5) rtpColor = 'var(--success)';
    else if (deviationPct <= 15) rtpColor = 'oklch(0.65 0.16 60)'; // orange
    else rtpColor = 'var(--danger)';
  }

  const toggle = async () => {
    setBusy(true);
    try {
      await adminFetch('/api/admin/games/' + g.id + '/active', {
        method: 'PUT',
        body: JSON.stringify({ active: g.active ? 0 : 1 }),
      });
      push((g.active ? t.tx('已暂停 ', 'Paused ') : t.tx('已启用 ', 'Enabled ')) + g.id);
      onAction();
    } catch (e) { push(t.tx('操作失败：', 'Action failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };

  return (
    <tr>
      <td data-label={t.tx('游戏', 'Game')}>
        <div style={{ fontWeight: 500 }}>{g.name}</div>
        <div className="text-mono text-faint" style={{ fontSize: 10 }}>{g.id}{g.vendor ? ' · ' + g.vendor : ''}</div>
      </td>
      <td data-label={t.tx('厂商', 'Provider')}><span className="badge outline">{g.provider_id || 'internal'}</span></td>
      <td data-label={t.tx('分类', 'Category')}><span className="badge outline">{g.category || '—'}</span></td>
      <td data-label={t.tx('RTP 配置', 'RTP Config')} className="tabular">{cfgRtp ? (cfgRtp * 100).toFixed(2) + '%' : '—'}</td>
      <td data-label={t.tx('RTP 实际', 'RTP Actual')} className="tabular" style={{ color: rtpColor, fontWeight: 600 }}>
        {realRtp != null ? (realRtp * 100).toFixed(2) + '%' : '—'}
        {deviationPct != null && <span className="text-faint" style={{ fontSize: 10, marginLeft: 4 }}>({deviationPct.toFixed(1)}%{t.tx('偏', ' dev')})</span>}
      </td>
      <td data-label={t.tx('流水', 'Stake')} className="right tabular">{stat ? '¥' + fmtNum(Math.round(stat.total_bet || 0)) : '—'}</td>
      <td data-label={t.tx('玩家', 'Players')} className="right tabular">{stat ? fmtNum(stat.players || 0) : '—'}</td>
      <td data-label={t.tx('状态', 'Status')}>{g.active ? <span className="badge success">{t.tx('运行', 'Running')}</span> : <span className="badge outline">{t.tx('暂停', 'Paused')}</span>}</td>
      <td data-label={t.tx('操作', 'Actions')} className="tbl-actions">
        <button className="btn btn-xs" disabled={busy} onClick={toggle}>
          {g.active ? t.tx('暂停', 'Pause') : t.tx('启用', 'Enable')}
        </button>
      </td>
    </tr>
  );
}

function AddGameModal({ onClose, push, onCreated, t }) {
  const [form, setForm] = React.useState({
    id: '', name: '', category: 'slot', vendor: '',
    launch_url: '', rtp: 0.96, min_bet: 1, max_bet: 1000,
  });
  const [busy, setBusy] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const submit = async () => {
    if (!form.id.trim() || !form.name.trim()) { push(t.tx('id / name 必填', 'id / name required')); return; }
    setBusy(true);
    try {
      await adminFetch('/api/admin/games', {
        method: 'POST',
        body: JSON.stringify({
          ...form,
          rtp: Number(form.rtp),
          min_bet: Number(form.min_bet),
          max_bet: Number(form.max_bet),
        }),
      });
      push(t.tx('已添加 ', 'Added ') + form.id);
      onCreated();
    } catch (e) { push(t.tx('添加失败：', 'Add failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };

  return (
    <Modal open={true} onClose={onClose} title={t.tx('添加游戏', 'Add Game')} width={520}
           footer={<>
             <button className="btn btn-sm" onClick={onClose}>{t.tx('取消', 'Cancel')}</button>
             <button className="btn btn-pri btn-sm" disabled={busy} onClick={submit}>{busy ? t.tx('提交中…', 'Submitting…') : t.tx('添加', 'Add')}</button>
           </>}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Field label={t.tx('ID (唯一)', 'ID (unique)')}><input className="input" value={form.id} onChange={(e) => set('id', e.target.value)} placeholder={t.tx('如 slot-fortune', 'e.g. slot-fortune')} /></Field>
        <Field label={t.tx('名称', 'Name')}><input className="input" value={form.name} onChange={(e) => set('name', e.target.value)} /></Field>
        <Field label={t.tx('分类', 'Category')}>
          <select className="select" value={form.category} onChange={(e) => set('category', e.target.value)}>
            <option value="slot">{t.tx('老虎机', 'Slot')}</option>
            <option value="table">{t.tx('桌面', 'Table')}</option>
            <option value="fish">{t.tx('捕鱼', 'Fish')}</option>
            <option value="card">{t.tx('扑克', 'Card')}</option>
            <option value="arcade">{t.tx('街机', 'Arcade')}</option>
          </select>
        </Field>
        <Field label={t.tx('厂商', 'Vendor')}><input className="input" value={form.vendor} onChange={(e) => set('vendor', e.target.value)} placeholder={t.tx('如 PG / JILI', 'e.g. PG / JILI')} /></Field>
        <Field label={t.tx('启动 URL', 'Launch URL')}><input className="input" value={form.launch_url} onChange={(e) => set('launch_url', e.target.value)} placeholder="https://…" /></Field>
        <Field label={t.tx('RTP (0~1)', 'RTP (0~1)')}><input className="input" type="number" step="0.001" min="0" max="1" value={form.rtp} onChange={(e) => set('rtp', e.target.value)} /></Field>
        <Field label={t.tx('最小投注', 'Min Bet')}><input className="input" type="number" min="0" value={form.min_bet} onChange={(e) => set('min_bet', e.target.value)} /></Field>
        <Field label={t.tx('最大投注', 'Max Bet')}><input className="input" type="number" min="0" value={form.max_bet} onChange={(e) => set('max_bet', e.target.value)} /></Field>
      </div>
    </Modal>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// GAMES · 报表 (周期统计 / 玩家记录 / 排行榜 / 结算单)
// ════════════════════════════════════════════════════════════════════════════
const REPORT_TABS = (t) => [
  ['today',      t.tx('当日',     'Today')],
  ['period',     t.tx('周期统计', 'Period')],
  ['player',     t.tx('玩家记录', 'Player')],
  ['top',        t.tx('排行榜',   'Top')],
  ['settle',     t.tx('结算单',   'Settlement')],
];

function todayISO(offsetDays = 0) {
  const d = new Date();
  d.setUTCDate(d.getUTCDate() + offsetDays);
  return d.toISOString().slice(0, 10);
}

async function downloadCSV(path, fname) {
  // adminFetch always JSON-parses, so go through raw fetch with the same auth
  // headers and turn the body into a Blob to trigger the browser save.
  const { API_BASE, authHeaders } = window.NebulaAdmin;
  const r = await fetch(API_BASE() + path, { headers: authHeaders() });
  if (!r.ok) {
    let body = null;
    try { body = await r.json(); } catch (e) {}
    const err = new Error('HTTP ' + r.status); err.status = r.status; err.body = body;
    throw err;
  }
  const blob = await r.blob();
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = fname;
  document.body.appendChild(a); a.click(); a.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

function GamesReports({ t, push }) {
  const [sub, setSub] = React.useState('today');
  const [providers] = useAdminPoll(pollKey('/api/admin/providers', 0), 30000, true);
  const provList = (providers && providers.providers) || [];

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('游戏报表', 'Game Reports')}</h1>
          <div className="sub">{t.tx('当日运营 · 日/周/月统计 · 玩家流水 · 排行榜 · 厂商结算单', 'Live dashboard · Daily/Weekly/Monthly stats · Player history · Top lists · Provider settlement')}</div>
        </div>
      </div>

      <div className="tabs">
        {REPORT_TABS(t).map(([k, label]) => (
          <div key={k} className={'tab' + (sub === k ? ' active' : '')} onClick={() => setSub(k)}>{label}</div>
        ))}
      </div>

      {sub === 'today' && <ReportYesterday t={t} push={push} />}
      {sub === 'period' && <ReportPeriod t={t} push={push} provList={provList} />}
      {sub === 'player' && <ReportPlayer t={t} push={push} provList={provList} />}
      {sub === 'top'    && <ReportTop    t={t} push={push} provList={provList} />}
      {sub === 'settle' && <ReportSettle t={t} push={push} provList={provList} />}
    </div>
  );
}

// ── 昨日 (Yesterday) — single-day operational dashboard ──────────────────────
function ReportYesterday({ t, push }) {
  // Default to today (live); date picker lets user back-fill any past day.
  const [date, setDate] = React.useState(todayISO(0));
  const [data, setData] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [anomalyOpen, setAnomalyOpen] = React.useState(null);

  const load = async () => {
    setBusy(true);
    try {
      const r = await adminFetch('/api/admin/games/reports/yesterday?date=' + encodeURIComponent(date));
      setData(r);
    } catch (e) { push(t.tx('加载失败：', 'Load failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const exportCsv = async () => {
    try { await downloadCSV('/api/admin/games/reports/yesterday?date=' + encodeURIComponent(date) + '&format=csv', 'yesterday-' + date + '.csv'); }
    catch (e) { push(t.tx('导出失败：', 'Export failed: ') + (e.body?.error || e.message)); }
  };
  React.useEffect(() => { load(); /* eslint-disable-next-line */ }, []);

  const totals = (data && data.totals) || {};
  const dod = (data && data.dod) || {};
  const wow = (data && data.wow) || {};
  const byProv = (data && data.by_provider) || [];
  const winners = (data && data.top_winners) || [];
  const losers = (data && data.top_losers) || [];
  const anomalies = (data && data.anomalies) || [];

  return (
    <div className="stack">
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'end' }}>
          <Field label={t.tx('报表日期', 'Report Date')}>
            <DateRangePicker t={t} mode="single" date={date} onChange={(d) => setDate(d)} size="sm" />
          </Field>
          <button className="btn btn-pri btn-sm" disabled={busy} onClick={load}>{busy ? t.tx('查询中…', 'Loading…') : t.tx('刷新', 'Refresh')}</button>
          <button className="btn btn-sm" onClick={exportCsv}>{t.tx('导出 CSV', 'Export CSV')}</button>
          {data && (
            <div className="text-faint" style={{ marginLeft: 'auto', fontSize: 11 }}>
              {t.tx('对比基线', 'Baselines')}: DoD {data.compare_dod} · WoW {data.compare_wow}
            </div>
          )}
        </div>
      </div>

      {data && (
        <>
          {/* 4 KPI 大卡 */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12 }}>
            <KpiCard t={t} label={t.tx('总投注', 'Total Bet')} value={'¥' + fmtNum(Math.round(totals.bet_amount || 0))} dod={dod.bet_amount_pct} wow={wow.bet_amount_pct} />
            <KpiCard t={t} label={t.tx('总派彩', 'Total Win')} value={'¥' + fmtNum(Math.round(totals.win_amount || 0))} dod={dod.win_amount_pct} wow={wow.win_amount_pct} />
            <KpiCard t={t} label={t.tx('GGR', 'GGR')} value={'¥' + fmtNum(Math.round(totals.ggr || 0))} dod={dod.ggr_pct} wow={wow.ggr_pct} valueColor={(totals.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)'} />
            <KpiCard t={t} label={t.tx('RTP', 'RTP')} value={((totals.rtp || 0) * 100).toFixed(2) + '%'} dodDelta={dod.rtp_delta} wowDelta={wow.rtp_delta} />
          </div>

          {/* 辅助 KPI */}
          <div className="card" style={{ padding: '10px 14px', display: 'flex', gap: 18, fontSize: 12, flexWrap: 'wrap' }}>
            <span>{t.tx('下注次数', 'Bet Count')}: <b className="tabular">{fmtNum(totals.bet_count || 0)}</b></span>
            <span>{t.tx('派彩次数', 'Win Count')}: <b className="tabular">{fmtNum(totals.win_count || 0)}</b></span>
            <span>{t.tx('会话数', 'Sessions')}: <b className="tabular">{fmtNum(totals.session_count || 0)}</b></span>
            <span>{t.tx('独立用户', 'Unique Users')}: <b className="tabular">{fmtNum(totals.unique_users || 0)}</b></span>
          </div>

          {/* 异常告警 strip */}
          {anomalies.length > 0 && (
            <div className="card" style={{ padding: 12 }}>
              <div style={{ fontWeight: 600, fontSize: 12, marginBottom: 8 }}>
                {t.tx('异常告警', 'Anomalies')} <span className="text-faint">({anomalies.length})</span>
              </div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                {anomalies.map((a, i) => {
                  const sevColor = a.severity === 'high' ? 'var(--danger)' : a.severity === 'mid' ? 'var(--warning)' : 'var(--info)';
                  const sevBg = a.severity === 'high' ? 'var(--danger-soft)' : a.severity === 'mid' ? 'var(--warning-soft)' : 'var(--info-soft)';
                  const label = anomalyLabel(t, a);
                  return (
                    <div key={i} title={a.detail || ''}
                         onClick={() => setAnomalyOpen(anomalyOpen === i ? null : i)}
                         style={{ padding: '4px 10px', borderRadius: 14, fontSize: 11, cursor: 'pointer',
                                  background: sevBg, color: sevColor, border: '1px solid ' + sevColor }}>
                      {label}
                    </div>
                  );
                })}
              </div>
              {anomalyOpen != null && anomalies[anomalyOpen] && (
                <div style={{ marginTop: 10, padding: 10, background: 'var(--bg-hover)', borderRadius: 6, fontSize: 11 }}>
                  <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{JSON.stringify(anomalies[anomalyOpen], null, 2)}</pre>
                </div>
              )}
            </div>
          )}

          {/* 左右两栏：厂商分布 / Top 榜单 */}
          <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 12 }}>
            <div className="card">
              <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', fontWeight: 600, fontSize: 13 }}>
                {t.tx('厂商分布', 'Provider Breakdown')}
              </div>
              <div className="tbl-scroll">
              <table className="tbl tbl-stack">
                <thead><tr>
                  <th>{t.tx('厂商', 'Provider')}</th>
                  <th className="right">{t.tx('下注', 'Bet')}</th>
                  <th className="right">{t.tx('派彩', 'Win')}</th>
                  <th className="right">{t.tx('GGR', 'GGR')}</th>
                  <th className="right">{t.tx('RTP', 'RTP')}</th>
                  <th className="right">{t.tx('占比', 'Share')}</th>
                </tr></thead>
                <tbody>
                  {byProv.length === 0 && <tr><td colSpan={6}><div className="empty">{t.tx('暂无数据', 'No data')}</div></td></tr>}
                  {byProv.map(p => (
                    <tr key={p.provider_id}>
                      <td data-label={t.tx('厂商', 'Provider')}><span className="badge outline">{p.provider_id}</span> {p.name || ''}</td>
                      <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(p.bet || 0))}</td>
                      <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(p.win || 0))}</td>
                      <td data-label={t.tx('GGR', 'GGR')} className="right tabular" style={{ color: (p.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(p.ggr || 0))}</td>
                      <td data-label={t.tx('RTP', 'RTP')} className="right tabular">{((p.rtp || 0) * 100).toFixed(2)}%</td>
                      <td data-label={t.tx('占比', 'Share')} className="right tabular">{(p.share_pct || 0).toFixed(1)}%</td>
                    </tr>
                  ))}
                </tbody>
              </table>
              </div>
            </div>

            <div className="stack">
              <TopPlayerTable t={t} title={t.tx('Top 10 大赢家', 'Top 10 Winners')} rows={winners} positive />
              <TopPlayerTable t={t} title={t.tx('Top 10 大输家', 'Top 10 Losers')} rows={losers} positive={false} />
            </div>
          </div>
        </>
      )}
    </div>
  );
}

function KpiCard({ t, label, value, valueColor, dod, wow, dodDelta, wowDelta }) {
  const renderDelta = (v, isDelta) => {
    if (v == null) return null;
    const up = v > 0;
    const flat = v === 0;
    const color = flat ? 'var(--text-muted)' : (up ? 'var(--success)' : 'var(--danger)');
    const arrow = flat ? '·' : (up ? '↑' : '↓');
    const txt = isDelta
      ? (up ? '+' : '') + (v * 100).toFixed(2) + 'pp'
      : (up ? '+' : '') + v.toFixed(1) + '%';
    return <span style={{ color, fontWeight: 500 }}>{arrow} {txt}</span>;
  };
  return (
    <div className="card" style={{ padding: 14, minWidth: 200 }}>
      <div className="text-muted" style={{ fontSize: 11, marginBottom: 6 }}>{label}</div>
      <div className="tabular" style={{ fontSize: 32, fontWeight: 700, lineHeight: 1.1, color: valueColor || 'var(--text)' }}>{value}</div>
      <div style={{ display: 'flex', gap: 10, fontSize: 11, marginTop: 8, flexWrap: 'wrap' }}>
        <span className="text-faint">DoD</span> {renderDelta(dodDelta != null ? dodDelta : dod, dodDelta != null)}
        <span className="text-faint" style={{ marginLeft: 6 }}>WoW</span> {renderDelta(wowDelta != null ? wowDelta : wow, wowDelta != null)}
      </div>
    </div>
  );
}

function TopPlayerTable({ t, title, rows, positive }) {
  return (
    <div className="card">
      <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', fontWeight: 600, fontSize: 13 }}>{title}</div>
      <div className="tbl-scroll">
      <table className="tbl tbl-stack">
        <thead><tr>
          <th style={{ width: 36 }}>#</th>
          <th>{t.tx('用户', 'User')}</th>
          <th className="right">{t.tx('净盈亏', 'Net')}</th>
          <th className="right">{t.tx('下注', 'Bet')}</th>
          <th className="right">{t.tx('派彩', 'Win')}</th>
          <th className="right">{t.tx('会话', 'Sess')}</th>
        </tr></thead>
        <tbody>
          {rows.length === 0 && <tr><td colSpan={6}><div className="empty">{t.tx('暂无数据', 'No data')}</div></td></tr>}
          {rows.map((r, i) => (
            <tr key={r.user_id}>
              <td data-label="#" className="text-faint">{i + 1}</td>
              <td data-label={t.tx('用户', 'User')}>
                <div>{r.user_name || r.user_id}</div>
                <div className="text-mono text-faint" style={{ fontSize: 10 }}>{r.user_id}</div>
              </td>
              <td data-label={t.tx('净盈亏', 'Net')} className="right tabular" style={{ color: positive ? 'var(--success)' : 'var(--danger)', fontWeight: 600 }}>
                {positive ? '+' : ''}¥{fmtNum(Math.round(r.net || 0))}
              </td>
              <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(r.bet || 0))}</td>
              <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(r.win || 0))}</td>
              <td data-label={t.tx('会话', 'Sess')} className="right tabular">{r.sessions || 0}</td>
            </tr>
          ))}
        </tbody>
      </table>
      </div>
    </div>
  );
}

function anomalyLabel(t, a) {
  if (a.kind === 'rtp_off_target') {
    return t.tx(a.provider_id + ' RTP 偏离', a.provider_id + ' RTP off');
  }
  if (a.kind === 'large_single_win') {
    return t.tx('大额单赢 ¥' + fmtNum(Math.round(a.amount || 0)), 'Large win ¥' + fmtNum(Math.round(a.amount || 0)));
  }
  if (a.kind === 'new_game_first_day') {
    return t.tx('新游戏 ' + (a.game_code || '') + ' 首日', 'New game ' + (a.game_code || ''));
  }
  return a.kind;
}

function ReportPeriod({ t, push, provList }) {
  const [from, setFrom] = React.useState(todayISO(-6));
  const [to, setTo] = React.useState(todayISO(0));
  const [period, setPeriod] = React.useState('daily');
  const [provider, setProvider] = React.useState('');
  const [gameId, setGameId] = React.useState('');
  const [data, setData] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const buildQS = (extra) => {
    const qs = new URLSearchParams({ from, to, period });
    if (provider) qs.set('provider', provider);
    if (gameId) qs.set('game_id', gameId);
    if (extra) Object.entries(extra).forEach(([k, v]) => qs.set(k, v));
    return qs.toString();
  };

  const load = async () => {
    setBusy(true);
    try {
      const r = await adminFetch('/api/admin/games/reports/period?' + buildQS());
      setData(r);
    } catch (e) { push(t.tx('加载失败：', 'Load failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const exportCsv = async () => {
    try { await downloadCSV('/api/admin/games/reports/period?' + buildQS({ format: 'csv' }), 'games-period-' + period + '-' + from + '-' + to + '.csv'); }
    catch (e) { push(t.tx('导出失败：', 'Export failed: ') + (e.body?.error || e.message)); }
  };

  React.useEffect(() => { load(); /* eslint-disable-next-line */ }, []);

  const periods = (data && data.periods) || [];
  const totals = (data && data.totals) || {};
  const breakdown = (data && data.provider_breakdown) || [];
  const maxGGR = periods.reduce((m, p) => Math.max(m, Math.abs(p.ggr || 0)), 1);

  return (
    <div className="stack">
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'end' }}>
          <Field label={t.tx('粒度', 'Bucket')}>
            <select className="select" value={period} onChange={(e) => setPeriod(e.target.value)}>
              <option value="daily">{t.tx('日', 'Daily')}</option>
              <option value="weekly">{t.tx('周', 'Weekly')}</option>
              <option value="monthly">{t.tx('月', 'Monthly')}</option>
            </select>
          </Field>
          <Field label={t.tx('日期范围', 'Date Range')}>
            <DateRangePicker t={t} from={from} to={to} onChange={(f, tt) => { setFrom(f); setTo(tt); }} size="sm" />
          </Field>
          <Field label={t.tx('厂商', 'Provider')}>
            <select className="select" value={provider} onChange={(e) => setProvider(e.target.value)}>
              <option value="">{t.tx('全部', 'All')}</option>
              {provList.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
            </select>
          </Field>
          <Field label={t.tx('游戏 ID', 'Game ID')}><input className="input" value={gameId} onChange={(e) => setGameId(e.target.value)} placeholder={t.tx('可选', 'optional')} /></Field>
          <button className="btn btn-pri btn-sm" disabled={busy} onClick={load}>{busy ? t.tx('查询中…', 'Loading…') : t.tx('查询', 'Query')}</button>
          <button className="btn btn-sm" onClick={exportCsv}>{t.tx('导出 CSV', 'Export CSV')}</button>
        </div>
      </div>

      {data && (
        <div className="card">
          <div style={{ padding: '12px 14px', borderBottom: '1px solid var(--border)', display: 'flex', gap: 18, fontSize: 12 }}>
            <span>{t.tx('合计下注', 'Total Bet')}: <b className="tabular">¥{fmtNum(Math.round(totals.bet_amount || 0))}</b></span>
            <span>{t.tx('合计派彩', 'Total Win')}: <b className="tabular">¥{fmtNum(Math.round(totals.win_amount || 0))}</b></span>
            <span>{t.tx('GGR', 'GGR')}: <b className="tabular" style={{ color: (totals.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(totals.ggr || 0))}</b></span>
            <span>{t.tx('RTP', 'RTP')}: <b className="tabular">{((totals.rtp || 0) * 100).toFixed(2)}%</b></span>
            <span>{t.tx('会话', 'Sessions')}: <b className="tabular">{fmtNum(totals.session_count || 0)}</b></span>
            <span>{t.tx('独立用户', 'Unique Users')}: <b className="tabular">{fmtNum(totals.unique_users || 0)}</b></span>
          </div>
          <div className="tbl-scroll">
          <table className="tbl tbl-stack">
            <thead><tr>
              <th>{t.tx('周期', 'Period')}</th>
              <th className="right">{t.tx('下注', 'Bet')}</th>
              <th className="right">{t.tx('派彩', 'Win')}</th>
              <th className="right">{t.tx('GGR', 'GGR')}</th>
              <th className="right">{t.tx('RTP', 'RTP')}</th>
              <th className="right">{t.tx('会话', 'Sessions')}</th>
              <th className="right">{t.tx('用户', 'Users')}</th>
              <th>{t.tx('GGR 走势', 'GGR Trend')}</th>
            </tr></thead>
            <tbody>
              {periods.length === 0 && <tr><td colSpan={8}><div className="empty">{t.tx('暂无数据', 'No data')}</div></td></tr>}
              {periods.map((p) => {
                const wPct = (Math.abs(p.ggr || 0) / maxGGR) * 100;
                const color = (p.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)';
                return (
                  <tr key={p.period}>
                    <td data-label={t.tx('周期', 'Period')} className="text-mono">{p.period}</td>
                    <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(p.bet_amount || 0))}</td>
                    <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(p.win_amount || 0))}</td>
                    <td data-label={t.tx('GGR', 'GGR')} className="right tabular" style={{ color }}>¥{fmtNum(Math.round(p.ggr || 0))}</td>
                    <td data-label={t.tx('RTP', 'RTP')} className="right tabular">{((p.rtp || 0) * 100).toFixed(2)}%</td>
                    <td data-label={t.tx('会话', 'Sessions')} className="right tabular">{fmtNum(p.session_count || 0)}</td>
                    <td data-label={t.tx('用户', 'Users')} className="right tabular">{fmtNum(p.unique_users || 0)}</td>
                    <td data-label={t.tx('GGR 走势', 'GGR Trend')}>
                      <div style={{ width: 180, height: 8, background: 'var(--bg-hover)', borderRadius: 4, overflow: 'hidden' }}>
                        <div style={{ width: wPct + '%', height: '100%', background: color, transition: 'width .3s' }} />
                      </div>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
          </div>
        </div>
      )}

      {breakdown.length > 0 && (
        <div className="card">
          <div style={{ padding: '12px 14px', borderBottom: '1px solid var(--border)', fontWeight: 600 }}>{t.tx('厂商分布', 'Provider Breakdown')}</div>
          <div className="tbl-scroll">
          <table className="tbl tbl-stack">
            <thead><tr>
              <th>{t.tx('厂商', 'Provider')}</th>
              <th className="right">{t.tx('下注', 'Bet')}</th>
              <th className="right">{t.tx('派彩', 'Win')}</th>
              <th className="right">{t.tx('GGR', 'GGR')}</th>
              <th className="right">{t.tx('RTP', 'RTP')}</th>
              <th className="right">{t.tx('GGR 占比', 'Share')}</th>
            </tr></thead>
            <tbody>
              {breakdown.map((b) => (
                <tr key={b.provider_id}>
                  <td data-label={t.tx('厂商', 'Provider')}><span className="badge outline">{b.provider_id}</span> {b.name || ''}</td>
                  <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(b.bet_amount || 0))}</td>
                  <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(b.win_amount || 0))}</td>
                  <td data-label={t.tx('GGR', 'GGR')} className="right tabular" style={{ color: (b.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(b.ggr || 0))}</td>
                  <td data-label={t.tx('RTP', 'RTP')} className="right tabular">{((b.rtp || 0) * 100).toFixed(2)}%</td>
                  <td data-label={t.tx('GGR 占比', 'Share')} className="right tabular">{((b.share || 0) * 100).toFixed(1)}%</td>
                </tr>
              ))}
            </tbody>
          </table>
          </div>
        </div>
      )}
    </div>
  );
}

function ReportPlayer({ t, push, provList }) {
  const [userId, setUserId] = React.useState('');
  const [from, setFrom] = React.useState(todayISO(-29));
  const [to, setTo] = React.useState(todayISO(0));
  const [provider, setProvider] = React.useState('');
  const [page, setPage] = React.useState(1);
  const [data, setData] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const buildQS = (extra) => {
    const qs = new URLSearchParams({ user_id: userId, from, to, page: String(page), limit: '50' });
    if (provider) qs.set('provider', provider);
    if (extra) Object.entries(extra).forEach(([k, v]) => qs.set(k, v));
    return qs.toString();
  };

  const load = async () => {
    if (!userId.trim()) { push(t.tx('请填写 user_id', 'user_id required')); return; }
    setBusy(true);
    try {
      const r = await adminFetch('/api/admin/games/player-history?' + buildQS());
      setData(r);
    } catch (e) { push(t.tx('加载失败：', 'Load failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const exportCsv = async () => {
    if (!userId.trim()) { push(t.tx('请填写 user_id', 'user_id required')); return; }
    try { await downloadCSV('/api/admin/games/player-history?' + buildQS({ format: 'csv' }), 'player-history-' + userId + '-' + from + '-' + to + '.csv'); }
    catch (e) { push(t.tx('导出失败：', 'Export failed: ') + (e.body?.error || e.message)); }
  };

  const rows = (data && data.rows) || [];
  const summary = (data && data.summary) || {};
  const total = (data && data.total) || 0;
  const totalPages = Math.max(1, Math.ceil(total / 50));

  return (
    <div className="stack">
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'end' }}>
          <Field label={t.tx('用户 ID', 'User ID')}><input className="input" value={userId} onChange={(e) => setUserId(e.target.value)} placeholder="U-xxx" /></Field>
          <Field label={t.tx('日期范围', 'Date Range')}>
            <DateRangePicker t={t} from={from} to={to} onChange={(f, tt) => { setFrom(f); setTo(tt); }} size="sm" />
          </Field>
          <Field label={t.tx('厂商', 'Provider')}>
            <select className="select" value={provider} onChange={(e) => setProvider(e.target.value)}>
              <option value="">{t.tx('全部', 'All')}</option>
              {provList.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
            </select>
          </Field>
          <button className="btn btn-pri btn-sm" disabled={busy} onClick={() => { setPage(1); load(); }}>{busy ? t.tx('查询中…', 'Loading…') : t.tx('查询', 'Query')}</button>
          <button className="btn btn-sm" onClick={exportCsv}>{t.tx('导出 CSV', 'Export CSV')}</button>
        </div>
      </div>

      {data && (
        <>
          <div className="card" style={{ padding: '12px 14px', display: 'flex', gap: 18, fontSize: 12, flexWrap: 'wrap' }}>
            <span>{t.tx('总下注', 'Total Bet')}: <b className="tabular">¥{fmtNum(Math.round(summary.total_bet || 0))}</b></span>
            <span>{t.tx('总派彩', 'Total Win')}: <b className="tabular">¥{fmtNum(Math.round(summary.total_win || 0))}</b></span>
            <span>{t.tx('GGR', 'GGR')}: <b className="tabular" style={{ color: (summary.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(summary.ggr || 0))}</b></span>
            <span>{t.tx('RTP', 'RTP')}: <b className="tabular">{((summary.rtp || 0) * 100).toFixed(2)}%</b></span>
            <span>{t.tx('会话数', 'Sessions')}: <b className="tabular">{fmtNum(summary.session_count || 0)}</b></span>
            <span className="text-mono text-faint" style={{ fontSize: 11 }}>{t.tx('首次', 'First')}: {summary.first_play || '—'}</span>
            <span className="text-mono text-faint" style={{ fontSize: 11 }}>{t.tx('最近', 'Last')}: {summary.last_play || '—'}</span>
          </div>
          <div className="card">
            <div className="tbl-scroll">
            <table className="tbl tbl-stack">
              <thead><tr>
                <th>{t.tx('时间', 'Time')}</th>
                <th>{t.tx('厂商', 'Provider')}</th>
                <th>{t.tx('游戏', 'Game')}</th>
                <th>{t.tx('类型', 'Kind')}</th>
                <th className="right">{t.tx('金额', 'Amount')}</th>
                <th>{t.tx('Round', 'Round')}</th>
                <th className="right">{t.tx('累计净盈亏', 'Cum. Net')}</th>
              </tr></thead>
              <tbody>
                {rows.length === 0 && <tr><td colSpan={7}><div className="empty">{t.tx('暂无记录', 'No records')}</div></td></tr>}
                {rows.map((r, i) => (
                  <tr key={r.round_id + ':' + r.kind + ':' + i}>
                    <td data-label={t.tx('时间', 'Time')} className="text-mono" style={{ fontSize: 11 }}>{r.ts}</td>
                    <td data-label={t.tx('厂商', 'Provider')}><span className="badge outline">{r.provider_id}</span></td>
                    <td data-label={t.tx('游戏', 'Game')}><span className="text-mono" style={{ fontSize: 11 }}>{r.game_code}</span>{r.game_name ? <div className="text-faint" style={{ fontSize: 10 }}>{r.game_name}</div> : null}</td>
                    <td data-label={t.tx('类型', 'Kind')}>{r.kind === 'bet'
                      ? <span className="badge" style={{ background: 'var(--danger-soft)', color: 'var(--danger)' }}>{t.tx('下注', 'Bet')}</span>
                      : <span className="badge" style={{ background: 'var(--success-soft)', color: 'var(--success)' }}>{t.tx('派彩', 'Win')}</span>}</td>
                    <td data-label={t.tx('金额', 'Amount')} className="right tabular">{r.kind === 'bet' ? '-' : '+'}¥{fmtNum(r.amount || 0)}</td>
                    <td data-label="Round" className="text-mono text-faint" style={{ fontSize: 11 }}>{r.round_id}</td>
                    <td data-label={t.tx('累计净盈亏', 'Cum. Net')} className="right tabular" style={{ color: (r.balance_after || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(r.balance_after || 0)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
            </div>
            {total > 50 && (
              <div style={{ display: 'flex', justifyContent: 'center', gap: 8, padding: 10, fontSize: 12 }}>
                <button className="btn btn-xs" disabled={page <= 1 || busy} onClick={() => { setPage(p => p - 1); setTimeout(load, 0); }}>{t.tx('上一页', 'Prev')}</button>
                <span style={{ alignSelf: 'center' }}>{page} / {totalPages}</span>
                <button className="btn btn-xs" disabled={page >= totalPages || busy} onClick={() => { setPage(p => p + 1); setTimeout(load, 0); }}>{t.tx('下一页', 'Next')}</button>
              </div>
            )}
          </div>
        </>
      )}
    </div>
  );
}

function ReportTop({ t, push, provList }) {
  const [dim, setDim] = React.useState('games');
  const [metric, setMetric] = React.useState('ggr');
  const [days, setDays] = React.useState(7);
  const [provider, setProvider] = React.useState('');
  const [data, setData] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const buildQS = (extra) => {
    const qs = new URLSearchParams({ dim, metric, days: String(days), limit: '20' });
    if (provider) qs.set('provider', provider);
    if (extra) Object.entries(extra).forEach(([k, v]) => qs.set(k, v));
    return qs.toString();
  };

  const load = async () => {
    setBusy(true);
    try {
      const r = await adminFetch('/api/admin/games/top?' + buildQS());
      setData(r);
    } catch (e) { push(t.tx('加载失败：', 'Load failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const exportCsv = async () => {
    try { await downloadCSV('/api/admin/games/top?' + buildQS({ format: 'csv' }), 'games-top-' + dim + '-' + days + 'd.csv'); }
    catch (e) { push(t.tx('导出失败：', 'Export failed: ') + (e.body?.error || e.message)); }
  };

  React.useEffect(() => { load(); /* eslint-disable-next-line */ }, [dim, metric, days, provider]);

  const rows = (data && data.rows) || [];
  const maxMetric = rows.reduce((m, r) => Math.max(m, Math.abs(r.metric || 0)), 1);

  return (
    <div className="stack">
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'end' }}>
          <Field label={t.tx('维度', 'Dimension')}>
            <select className="select" value={dim} onChange={(e) => setDim(e.target.value)}>
              <option value="games">{t.tx('游戏', 'Games')}</option>
              <option value="users">{t.tx('用户', 'Users')}</option>
            </select>
          </Field>
          <Field label={t.tx('指标', 'Metric')}>
            <select className="select" value={metric} onChange={(e) => setMetric(e.target.value)}>
              <option value="ggr">{t.tx('GGR', 'GGR')}</option>
              <option value="bet">{t.tx('下注额', 'Bet')}</option>
              <option value="win">{t.tx('派彩额', 'Win')}</option>
              <option value="sessions">{t.tx('会话数', 'Sessions')}</option>
            </select>
          </Field>
          <Field label={t.tx('时间窗', 'Window')}>
            <select className="select" value={days} onChange={(e) => setDays(Number(e.target.value))}>
              <option value={7}>{t.tx('近 7 日', 'Last 7d')}</option>
              <option value={30}>{t.tx('近 30 日', 'Last 30d')}</option>
            </select>
          </Field>
          <Field label={t.tx('厂商', 'Provider')}>
            <select className="select" value={provider} onChange={(e) => setProvider(e.target.value)}>
              <option value="">{t.tx('全部', 'All')}</option>
              {provList.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
            </select>
          </Field>
          <button className="btn btn-sm" disabled={busy} onClick={load}>{t.tx('刷新', 'Refresh')}</button>
          <button className="btn btn-sm" onClick={exportCsv}>{t.tx('导出 CSV', 'Export CSV')}</button>
        </div>
      </div>

      <div className="card">
        <div className="tbl-scroll">
        <table className="tbl tbl-stack">
          <thead><tr>
            <th style={{ width: 40 }}>#</th>
            <th>{dim === 'games' ? t.tx('游戏', 'Game') : t.tx('用户', 'User')}</th>
            <th className="right">{t.tx('下注', 'Bet')}</th>
            <th className="right">{t.tx('派彩', 'Win')}</th>
            <th className="right">{t.tx('GGR', 'GGR')}</th>
            <th className="right">{t.tx('会话', 'Sessions')}</th>
            <th>{t.tx('指标', 'Metric')}</th>
          </tr></thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan={7}><div className="empty">{t.tx('暂无数据', 'No data')}</div></td></tr>}
            {rows.map((r, i) => {
              const wPct = (Math.abs(r.metric || 0) / maxMetric) * 100;
              return (
                <tr key={r.key || (r.user_id || r.game_id) + ':' + i}>
                  <td data-label="#" className="tabular">{i + 1}</td>
                  <td data-label={dim === 'games' ? t.tx('游戏', 'Game') : t.tx('用户', 'User')}>
                    <div style={{ fontWeight: 500 }}>{r.label || (dim === 'games' ? r.game_id : r.user_id)}</div>
                    <div className="text-faint text-mono" style={{ fontSize: 10 }}>
                      {dim === 'games' ? (r.provider_id + ' · ' + r.game_id) : r.user_id}
                    </div>
                  </td>
                  <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(r.bet_amount || 0))}</td>
                  <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(r.win_amount || 0))}</td>
                  <td data-label={t.tx('GGR', 'GGR')} className="right tabular" style={{ color: (r.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(r.ggr || 0))}</td>
                  <td data-label={t.tx('会话', 'Sessions')} className="right tabular">{fmtNum(r.session_count || 0)}</td>
                  <td data-label={t.tx('指标', 'Metric')}>
                    <div style={{ width: 200, height: 8, background: 'var(--bg-hover)', borderRadius: 4, overflow: 'hidden' }}>
                      <div style={{ width: wPct + '%', height: '100%', background: 'var(--brand)' }} />
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
        </div>
      </div>
    </div>
  );
}

function ReportSettle({ t, push, provList }) {
  const [provider, setProvider] = React.useState('pgsoft');
  const [from, setFrom] = React.useState(todayISO(-29));
  const [to, setTo] = React.useState(todayISO(0));
  const [data, setData] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [shareEdit, setShareEdit] = React.useState('');
  const [savingShare, setSavingShare] = React.useState(false);

  const buildQS = (extra) => {
    const qs = new URLSearchParams({ provider, from, to });
    if (extra) Object.entries(extra).forEach(([k, v]) => qs.set(k, v));
    return qs.toString();
  };

  const generate = async () => {
    if (!provider) { push(t.tx('请选择厂商', 'Pick a provider')); return; }
    setBusy(true);
    try {
      const r = await adminFetch('/api/admin/games/settlement?' + buildQS());
      setData(r);
      setShareEdit(String(((r && r.totals && r.totals.platform_share_rate) || 0)));
    } catch (e) { push(t.tx('生成失败：', 'Failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const exportCsv = async () => {
    if (!provider) return;
    try { await downloadCSV('/api/admin/games/settlement?' + buildQS({ format: 'csv' }), 'settlement-' + provider + '-' + from + '-' + to + '.csv'); }
    catch (e) { push(t.tx('导出失败：', 'Export failed: ') + (e.body?.error || e.message)); }
  };
  const saveShareRate = async () => {
    const v = Number(shareEdit);
    if (!isFinite(v) || v < 0 || v > 1) { push(t.tx('比例需在 0~1 之间', 'Rate must be 0–1')); return; }
    setSavingShare(true);
    try {
      await adminFetch('/api/admin/providers/' + provider, {
        method: 'PUT',
        body: JSON.stringify({ platform_share_rate: v }),
      });
      push(t.tx('已更新平台分成率', 'Platform share rate updated'));
      await generate();
    } catch (e) { push(t.tx('更新失败：', 'Update failed: ') + (e.body?.error || e.message)); }
    finally { setSavingShare(false); }
  };

  const totals = (data && data.totals) || {};
  const byDay = (data && data.by_day) || [];

  return (
    <div className="stack">
      <div className="card" style={{ padding: 14 }}>
        <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'end' }}>
          <Field label={t.tx('厂商', 'Provider')}>
            <select className="select" value={provider} onChange={(e) => setProvider(e.target.value)}>
              {provList.map(p => <option key={p.id} value={p.id}>{p.name} ({p.id})</option>)}
            </select>
          </Field>
          <Field label={t.tx('日期范围', 'Date Range')}>
            <DateRangePicker t={t} from={from} to={to} onChange={(f, tt) => { setFrom(f); setTo(tt); }} size="sm" />
          </Field>
          <button className="btn btn-pri btn-sm" disabled={busy} onClick={generate}>{busy ? t.tx('生成中…', 'Generating…') : t.tx('生成', 'Generate')}</button>
          <button className="btn btn-sm" disabled={!data} onClick={exportCsv}>{t.tx('导出 CSV (财务)', 'Export CSV')}</button>
        </div>
      </div>

      {data && (
        <>
          <div className="card" style={{ padding: 14 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
              <div>
                <div style={{ fontWeight: 600, fontSize: 14 }}>{data.provider_name || provider} <span className="text-faint">· {provider}</span></div>
                <div className="text-faint" style={{ fontSize: 11 }}>{data.period_from} ~ {data.period_to}</div>
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span className="text-muted" style={{ fontSize: 12 }}>{t.tx('平台分成率', 'Platform Share')}</span>
                <input className="input" type="number" min="0" max="1" step="0.01" value={shareEdit} onChange={(e) => setShareEdit(e.target.value)} style={{ width: 90 }} />
                <button className="btn btn-xs" disabled={savingShare} onClick={saveShareRate}>{savingShare ? '…' : t.tx('保存', 'Save')}</button>
              </div>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: 12 }}>
              <SettleStat t={t} label={t.tx('记录下注', 'Bet Recorded')} value={'¥' + fmtNum(Math.round(totals.platform_bet_recorded || 0))} />
              <SettleStat t={t} label={t.tx('已派彩', 'Win Paid')}      value={'¥' + fmtNum(Math.round(totals.platform_win_paid || 0))} />
              <SettleStat t={t} label={t.tx('GGR', 'GGR')}              value={'¥' + fmtNum(Math.round(totals.ggr || 0))} color={(totals.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)'} />
              <SettleStat t={t} label={t.tx('平台分成', 'Platform Share')} value={'¥' + fmtNum(Math.round(totals.platform_share_amount || 0))} sub={((totals.platform_share_rate || 0) * 100).toFixed(1) + '%'} />
              <SettleStat t={t} label={t.tx('厂商分成', 'Provider Share')} value={'¥' + fmtNum(Math.round(totals.provider_share_amount || 0))} />
              <SettleStat t={t} label={t.tx('回调事件', 'Callback Events')} value={fmtNum(totals.callback_event_count || 0)} />
            </div>
          </div>

          <div className="card">
            <div style={{ padding: '10px 14px', borderBottom: '1px solid var(--border)', fontWeight: 600 }}>{t.tx('按日明细', 'By Day')}</div>
            <div className="tbl-scroll">
            <table className="tbl tbl-stack">
              <thead><tr>
                <th>{t.tx('日期', 'Date')}</th>
                <th className="right">{t.tx('下注', 'Bet')}</th>
                <th className="right">{t.tx('派彩', 'Win')}</th>
                <th className="right">{t.tx('GGR', 'GGR')}</th>
                <th className="right">{t.tx('平台分成', 'Platform')}</th>
                <th className="right">{t.tx('厂商分成', 'Provider')}</th>
              </tr></thead>
              <tbody>
                {byDay.length === 0 && <tr><td colSpan={6}><div className="empty">{t.tx('暂无数据', 'No data')}</div></td></tr>}
                {byDay.map(d => (
                  <tr key={d.date}>
                    <td data-label={t.tx('日期', 'Date')} className="text-mono">{d.date}</td>
                    <td data-label={t.tx('下注', 'Bet')} className="right tabular">¥{fmtNum(Math.round(d.bet || 0))}</td>
                    <td data-label={t.tx('派彩', 'Win')} className="right tabular">¥{fmtNum(Math.round(d.win || 0))}</td>
                    <td data-label={t.tx('GGR', 'GGR')} className="right tabular" style={{ color: (d.ggr || 0) >= 0 ? 'var(--success)' : 'var(--danger)' }}>¥{fmtNum(Math.round(d.ggr || 0))}</td>
                    <td data-label={t.tx('平台分成', 'Platform')} className="right tabular">¥{fmtNum(Math.round(d.platform_share || 0))}</td>
                    <td data-label={t.tx('厂商分成', 'Provider')} className="right tabular">¥{fmtNum(Math.round(d.provider_share || 0))}</td>
                  </tr>
                ))}
              </tbody>
            </table>
            </div>
          </div>

          {(data.discrepancies || []).length > 0 && (
            <div className="card" style={{ padding: 14, borderColor: 'var(--danger)' }}>
              <div style={{ fontWeight: 600, color: 'var(--danger)', marginBottom: 8 }}>{t.tx('差异 (需对账)', 'Discrepancies')}</div>
              <pre style={{ fontSize: 11 }}>{JSON.stringify(data.discrepancies, null, 2)}</pre>
            </div>
          )}
        </>
      )}
    </div>
  );
}

function SettleStat({ label, value, sub, color }) {
  return (
    <div style={{ padding: 10, background: 'var(--bg-hover)', borderRadius: 8 }}>
      <div className="text-muted" style={{ fontSize: 11, marginBottom: 4 }}>{label}</div>
      <div className="tabular" style={{ fontWeight: 600, fontSize: 16, color: color || 'var(--text)' }}>{value}</div>
      {sub && <div className="text-faint" style={{ fontSize: 10, marginTop: 2 }}>{sub}</div>}
    </div>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// CMS · 短剧编目
// ════════════════════════════════════════════════════════════════════════════
function CMSScreen({ t, push }) {
  const [rkey, refetch] = useRefetchKey();
  const [data, err] = useAdminPoll(pollKey('/api/drama/series?limit=50&order=hot', rkey), 10000, true);
  const [selected, setSelected] = React.useState(null); // series code or null
  const [showNew, setShowNew] = React.useState(false);
  const series = (data && data.series) || [];

  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('短剧编目', 'Drama CMS')}</h1>
          <div className="sub">{t.tx('短剧库 · 上下架 · 集列表 + 视频 URL 编辑', 'Drama library · publish/unpublish · episodes + video URL editor')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm" onClick={refetch}><Icons.refresh /> {t.tx('刷新', 'Refresh')}</button>
          <button className="btn btn-pri btn-sm" onClick={() => setShowNew(true)}><Icons.plus /> {t.tx('新建剧目', 'New Drama')}</button>
        </div>
      </div>

      {err && <div className="card" style={{ padding: 16, color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败：', 'Load failed: ')}{String(err.message || err)}</div>}
      {!data && !err && <div className="card" style={{ padding: 32, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}

      {data && series.length === 0 && <div className="empty">{t.tx('还没有短剧 · 点击「新建剧目」', 'No dramas yet · click "New Drama"')}</div>}
      {data && series.length > 0 && (
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 14 }}>
          {series.map((s, i) => (
            <DramaCard key={s.code} s={s} idx={i} onOpen={() => setSelected(s.code)} push={push} onAction={refetch} t={t} />
          ))}
        </div>
      )}

      {selected && <DramaDetailModal code={selected} onClose={() => setSelected(null)} push={push} onChange={refetch} t={t} />}
      {showNew && <NewDramaModal onClose={() => setShowNew(false)} push={push} onCreated={() => { setShowNew(false); refetch(); }} t={t} />}
    </div>
  );
}

function DramaCard({ s, idx, onOpen, push, onAction, t }) {
  const [busy, setBusy] = React.useState(false);
  const setOnline = async (online) => {
    setBusy(true);
    try {
      await adminFetch('/api/admin/drama/series/' + s.code + '/' + (online ? 'online' : 'offline'), { method: 'POST' });
      push((online ? t.tx('已上架 ', 'Published ') : t.tx('已下架 ', 'Unpublished ')) + s.code);
      onAction();
    } catch (e) { push(t.tx('操作失败：', 'Action failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const isOnline = s.status === 'online' || s.status === 'live';
  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div onClick={onOpen} style={{
        aspectRatio: '3/4',
        background: `linear-gradient(135deg, oklch(0.55 0.18 ${(idx * 67) % 360}), oklch(0.35 0.20 ${(idx * 91) % 360}))`,
        position: 'relative', cursor: 'pointer'
      }}>
        <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(0,0,0,0) 50%, rgba(0,0,0,0.85))' }} />
        <div style={{ position: 'absolute', top: 8, left: 8, display: 'flex', gap: 4 }}>
          {isOnline ? <span className="badge success">{t.tx('上架', 'Live')}</span> : <span className="badge outline">{t.tx('下架', 'Off')}</span>}
          {s.category && <span className="badge" style={{ background: 'rgba(0,0,0,0.55)', color: 'white' }}>{s.category}</span>}
        </div>
        <div style={{ position: 'absolute', bottom: 10, left: 10, right: 10, color: 'white' }}>
          <div style={{ fontSize: 14, fontWeight: 700, textShadow: '0 1px 4px rgba(0,0,0,0.7)', lineHeight: 1.25, marginBottom: 4 }}>{s.title}</div>
          <div style={{ fontSize: 10, opacity: 0.9, display: 'flex', justifyContent: 'space-between' }}>
            <span>{s.total_episodes || 0}{t.tx('集 · 免 ', ' eps · free ')}{s.free_episodes || 0}</span>
            <span>¥{s.price_per_episode || 0}{t.tx('/集', '/ep')}</span>
          </div>
        </div>
      </div>
      <div style={{ padding: 10, display: 'flex', alignItems: 'center', gap: 6 }}>
        <span className="text-mono text-faint" style={{ fontSize: 10 }}>{s.code}</span>
        <span className="text-muted" style={{ fontSize: 10, marginLeft: 'auto' }}>{t.tx('热 ', 'Hot ')}{Math.round(s.hot_score || 0)}</span>
      </div>
      <div style={{ padding: '0 10px 10px', display: 'flex', gap: 4 }}>
        <button className="btn btn-xs" onClick={onOpen} style={{ flex: 1 }}>{t.tx('详情/编集', 'Details / Episodes')}</button>
        {isOnline
          ? <button className="btn btn-xs btn-danger" disabled={busy} onClick={() => setOnline(false)}>{t.tx('下架', 'Unpublish')}</button>
          : <button className="btn btn-xs btn-pri" disabled={busy} onClick={() => setOnline(true)}>{t.tx('上架', 'Publish')}</button>}
      </div>
    </div>
  );
}

function DramaDetailModal({ code, onClose, push, onChange, t }) {
  const [resp, setResp] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const load = React.useCallback(async () => {
    try {
      const j = await adminFetch('/api/drama/series/' + code);
      setResp(j);
    } catch (e) { setErr(e); }
  }, [code]);
  React.useEffect(() => { load(); }, [load]);

  const s = resp && (resp.series || resp);
  const episodes = (resp && resp.episodes) || (s && s.episodes) || [];

  return (
    <Modal open={true} onClose={onClose} title={s ? (t.tx('剧目 · ', 'Drama · ') + (s.title || code)) : t.tx('加载中…', 'Loading…')} width={720}
           footer={<button className="btn btn-sm" onClick={onClose}>{t.tx('关闭', 'Close')}</button>}>
      {err && <div style={{ color: 'var(--danger)', fontSize: 12 }}>{t.tx('加载失败', 'Load failed')}</div>}
      {!resp && !err && <div style={{ padding: 16, textAlign: 'center', color: 'var(--text-muted)', fontSize: 12 }}>{t.tx('加载中…', 'Loading…')}</div>}
      {s && (
        <div className="stack" style={{ gap: 14 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 10, fontSize: 12 }}>
            <div><div className="text-muted" style={{ fontSize: 11 }}>{t.tx('分类', 'Category')}</div><div>{s.category || '—'}</div></div>
            <div><div className="text-muted" style={{ fontSize: 11 }}>{t.tx('集数', 'Episodes')}</div><div>{s.total_episodes || 0}{t.tx(' 集', ' eps')}</div></div>
            <div><div className="text-muted" style={{ fontSize: 11 }}>{t.tx('免费', 'Free')}</div><div>{t.tx('前 ', 'First ')}{s.free_episodes || 0}{t.tx(' 集', ' eps')}</div></div>
            <div><div className="text-muted" style={{ fontSize: 11 }}>{t.tx('单价', 'Price')}</div><div>¥{s.price_per_episode || 0}</div></div>
          </div>
          {s.description && <div style={{ padding: 10, background: 'var(--bg-inset)', borderRadius: 6, fontSize: 12, lineHeight: 1.6 }}>{s.description}</div>}

          <div style={{ fontSize: 12, fontWeight: 600 }}>{t.tx('分集列表（编辑 video_url / 时长）', 'Episodes (edit video_url / duration)')}</div>
          <div style={{ maxHeight: 320, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6 }}>
            <EpisodeList code={code} episodes={episodes} total={s.total_episodes || 0} push={push} onSaved={() => { load(); onChange(); }} t={t} />
          </div>
        </div>
      )}
    </Modal>
  );
}

function EpisodeList({ code, episodes, total, push, onSaved, t }) {
  // Build a slot per episode_no 1..total, populated by existing data
  const map = {};
  episodes.forEach(e => { map[e.episode_no || e.ep || e.episode || e.index] = e; });
  const epNums = episodes.map(e => e.episode_no || 0);
  const maxEp = Math.max(total || 0, epNums.length ? Math.max.apply(null, epNums) : 0);
  const rows = [];
  for (let i = 1; i <= maxEp; i++) {
    rows.push({ ep: i, data: map[i] || null });
  }
  if (rows.length === 0) return <div className="empty">{t.tx('无集数', 'No episodes')}</div>;
  return (
    <div className="tbl-scroll">
    <table className="tbl tbl-stack" style={{ fontSize: 11 }}>
      <thead><tr><th style={{ width: 50 }}>{t.tx('集', 'Ep')}</th><th>{t.tx('视频 URL', 'Video URL')}</th><th style={{ width: 90 }}>{t.tx('时长(秒)', 'Duration (s)')}</th><th style={{ width: 80 }}></th></tr></thead>
      <tbody>
        {rows.map(r => <EpisodeRow key={r.ep} code={code} ep={r.ep} data={r.data} push={push} onSaved={onSaved} t={t} />)}
      </tbody>
    </table>
    </div>
  );
}

function EpisodeRow({ code, ep, data, push, onSaved, t }) {
  const [url, setUrl] = React.useState((data && data.video_url) || '');
  const [dur, setDur] = React.useState((data && data.duration_seconds) || 0);
  const [busy, setBusy] = React.useState(false);
  const save = async () => {
    setBusy(true);
    try {
      await adminFetch('/api/admin/drama/series/' + code + '/episode/' + ep, {
        method: 'PUT',
        body: JSON.stringify({ video_url: url, duration_seconds: Number(dur) || 0 }),
      });
      push(t.tx('第 ' + ep + ' 集已保存', 'Episode ' + ep + ' saved'));
      onSaved();
    } catch (e) { push(t.tx('保存失败：', 'Save failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  return (
    <tr>
      <td data-label={t.tx('集', 'Ep')} className="tabular">EP{String(ep).padStart(2, '0')}</td>
      <td data-label={t.tx('视频 URL', 'Video URL')}><input className="input" value={url} onChange={(e) => setUrl(e.target.value)} placeholder="https://cdn…/ep01.mp4" style={{ fontSize: 11 }} /></td>
      <td data-label={t.tx('时长(秒)', 'Duration (s)')}><input className="input" type="number" value={dur} onChange={(e) => setDur(e.target.value)} style={{ fontSize: 11 }} /></td>
      <td data-label={t.tx('操作', 'Actions')}><button className="btn btn-xs btn-pri" disabled={busy} onClick={save}>{t.tx('保存', 'Save')}</button></td>
    </tr>
  );
}

function NewDramaModal({ onClose, push, onCreated, t }) {
  const [form, setForm] = React.useState({
    code: '', title: '', description: '', category: '都市',
    total_episodes: 20, free_episodes: 3, price_per_episode: 1.99,
  });
  const [busy, setBusy] = React.useState(false);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const submit = async () => {
    if (!form.code.trim() || !form.title.trim()) { push(t.tx('code / title 必填', 'code / title required')); return; }
    setBusy(true);
    try {
      await adminFetch('/api/admin/drama/series', {
        method: 'POST',
        body: JSON.stringify({
          ...form,
          total_episodes: Number(form.total_episodes),
          free_episodes: Number(form.free_episodes),
          price_per_episode: Number(form.price_per_episode),
        }),
      });
      push(t.tx('已创建 ', 'Created ') + form.code);
      onCreated();
    } catch (e) { push(t.tx('创建失败：', 'Create failed: ') + (e.body?.error || e.message)); }
    finally { setBusy(false); }
  };
  const catLabels = {
    '都市': t.tx('都市', 'Urban'),
    '总裁': t.tx('总裁', 'CEO'),
    '重生': t.tx('重生', 'Rebirth'),
    '虐恋': t.tx('虐恋', 'Drama Romance'),
    '玄幻': t.tx('玄幻', 'Fantasy'),
    '逆袭': t.tx('逆袭', 'Comeback'),
    '悬疑': t.tx('悬疑', 'Mystery'),
    '古装': t.tx('古装', 'Historical'),
  };
  return (
    <Modal open={true} onClose={onClose} title={t.tx('新建短剧', 'New Drama')} width={560}
           footer={<>
             <button className="btn btn-sm" onClick={onClose}>{t.tx('取消', 'Cancel')}</button>
             <button className="btn btn-pri btn-sm" disabled={busy} onClick={submit}>{busy ? t.tx('创建中…', 'Creating…') : t.tx('创建', 'Create')}</button>
           </>}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Field label={t.tx('剧目代号 (唯一)', 'Drama Code (unique)')}><input className="input" value={form.code} onChange={(e) => set('code', e.target.value)} placeholder={t.tx('如 d-ceo-01', 'e.g. d-ceo-01')} /></Field>
        <Field label={t.tx('标题', 'Title')}><input className="input" value={form.title} onChange={(e) => set('title', e.target.value)} /></Field>
        <Field label={t.tx('分类', 'Category')}>
          <select className="select" value={form.category} onChange={(e) => set('category', e.target.value)}>
            {['都市','总裁','重生','虐恋','玄幻','逆袭','悬疑','古装'].map(c => <option key={c} value={c}>{catLabels[c] || c}</option>)}
          </select>
        </Field>
        <Field label={t.tx('总集数', 'Total Episodes')}><input className="input" type="number" min="1" value={form.total_episodes} onChange={(e) => set('total_episodes', e.target.value)} /></Field>
        <Field label={t.tx('免费集数', 'Free Episodes')}><input className="input" type="number" min="0" value={form.free_episodes} onChange={(e) => set('free_episodes', e.target.value)} /></Field>
        <Field label={t.tx('单集价 ¥', 'Price per Ep ¥')}><input className="input" type="number" step="0.01" min="0" value={form.price_per_episode} onChange={(e) => set('price_per_episode', e.target.value)} /></Field>
        <div style={{ gridColumn: 'span 2' }}>
          <Field label={t.tx('简介', 'Description')}><textarea className="input" rows={3} value={form.description} onChange={(e) => set('description', e.target.value)} /></Field>
        </div>
      </div>
    </Modal>
  );
}

// ════════════════════════════════════════════════════════════════════════════
// BRAND MATRIX · 多品牌矩阵 (UI-only, no backend yet)
// ════════════════════════════════════════════════════════════════════════════
function BrandMatrixScreen({ t, push }) {
  const [selected, setSelected] = React.useState('B-aurora');
  const [showCloner, setShowCloner] = React.useState(false);
  return (
    <div className="stack">
      <div className="page-h">
        <div>
          <h1>{t.tx('品牌矩阵', 'Brand Matrix')}</h1>
          <div className="sub">{t.tx('一套底盘开 N 个市场品牌 · 主题引擎 + 一键克隆 + 实时预览', 'One platform · N market brands · theme engine + one-click clone + live preview')}</div>
        </div>
        <div className="actions">
          <button className="btn btn-sm">{t.tx('主题模板库', 'Theme Templates')}</button>
          <button className="btn btn-sm" onClick={() => setShowCloner(true)}><Icons.copy /> {t.tx('克隆品牌', 'Clone Brand')}</button>
          <button className="btn btn-pri btn-sm" onClick={() => setShowCloner(true)}><Icons.plus /> {t.tx('新建品牌', 'New Brand')}</button>
        </div>
      </div>

      <div className="card">
        <div className="card-h">
          <h3>{t.tx('品牌列表 · ', 'Brands · ')}{BRANDS.length}{t.tx(' 个', '')}</h3>
          <div style={{ display: 'flex', gap: 4 }}>
            <button className="chip active">{t.tx('全部', 'All')}</button>
            <button className="chip">{t.tx('中国大陆', 'Mainland CN')}</button>
            <button className="chip">{t.tx('东南亚', 'SEA')}</button>
            <button className="chip">{t.tx('欧美', 'EU/US')}</button>
          </div>
        </div>
        <div style={{ padding: 16, display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 14 }}>
          {BRANDS.map((b) => (
            <BrandCard key={b.id} brand={b} selected={selected === b.id} onSelect={() => setSelected(b.id)} t={t} />
          ))}
          <div onClick={() => setShowCloner(true)}
               style={{ border: '2px dashed var(--border-strong)', borderRadius: 12, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 40, color: 'var(--text-muted)', fontSize: 12, cursor: 'default' }}>
            <Icons.plus />
            <div style={{ marginTop: 8 }}>{t.tx('克隆新品牌', 'Clone New Brand')}</div>
          </div>
        </div>
      </div>

      {selected && <BrandEditor brand={BRANDS.find(b => b.id === selected)} push={push} t={t} />}
      {showCloner && <BrandClonerModal onClose={() => setShowCloner(false)} push={push} t={t} />}
    </div>
  );
}

const BRANDS = [
  { id: 'B-aurora',   name: '极光娱乐',     domain: 'aurora.cn',     market: '中国大陆',  users: 248903, lang: 'zh-CN', currency: 'CNY',  primary: '#FF3D7F', bg: '#0A0A14', logo: 'A', status: 'live' },
  { id: 'B-stellar',  name: 'Stellar Play', domain: 'stellar.hk',    market: '香港',      users: 89231,  lang: 'zh-HK', currency: 'HKD',  primary: '#6FE7FF', bg: '#0F1424', logo: 'S', status: 'live' },
  { id: 'B-nova',     name: 'Nova Media',   domain: 'nova.ph',       market: '菲律宾',    users: 32108,  lang: 'en-PH', currency: 'PHP',  primary: '#F5C648', bg: '#1A1004', logo: 'N', status: 'live' },
  { id: 'B-sunrise',  name: '红日传媒',     domain: 'sunrise.live',  market: '中国大陆',  users: 156804, lang: 'zh-CN', currency: 'CNY',  primary: '#FF6B4D', bg: '#1A0A0A', logo: '红', status: 'live' },
  { id: 'B-galaxy',   name: '银河直播',     domain: 'galaxy.vn',     market: '越南',      users: 412904, lang: 'vi-VN', currency: 'VND',  primary: '#9E5AFB', bg: '#0F0A24', logo: 'G', status: 'live' },
  { id: 'B-pluto',    name: 'Pluto Streams',domain: 'pluto.th',      market: '泰国',      users: 12490,  lang: 'th-TH', currency: 'THB',  primary: '#2BD37B', bg: '#0A1A0F', logo: 'P', status: 'maintenance' },
  { id: 'B-orion',    name: 'Orion Asia',   domain: 'orion.id',      market: '印尼',      users: 84200,  lang: 'id-ID', currency: 'IDR',  primary: '#FF8A3D', bg: '#1A0F0A', logo: 'O', status: 'live' },
  { id: 'B-comet',    name: 'Comet Crypto', domain: 'comet.io',      market: '加密圈',    users: 28400,  lang: 'en-US', currency: 'USDT', primary: '#26A17B', bg: '#04140A', logo: 'C', status: 'live' },
];

function BrandCard({ brand, selected, onSelect, t }) {
  return (
    <div onClick={onSelect}
         style={{
           border: '2px solid ' + (selected ? brand.primary : 'var(--border)'),
           borderRadius: 12, overflow: 'hidden',
           background: 'var(--bg-card)', cursor: 'default'
         }}>
      <div style={{ background: brand.bg, padding: 12, position: 'relative', aspectRatio: '4/3' }}>
        <div style={{ background: 'rgba(255,255,255,0.04)', borderRadius: 8, padding: 8, height: '100%', display: 'flex', flexDirection: 'column', gap: 6 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <div style={{ width: 22, height: 22, borderRadius: 6, background: brand.primary, color: 'white', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700 }}>{brand.logo}</div>
            <span style={{ fontSize: 9, color: 'rgba(255,255,255,0.7)' }}>{brand.name}</span>
          </div>
          <div style={{ height: 30, background: `linear-gradient(135deg, ${brand.primary}, ${brand.primary}aa)`, borderRadius: 4 }} />
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4 }}>
            {Array.from({ length: 8 }).map((_, i) => (
              <div key={i} style={{ aspectRatio: '1', background: 'rgba(255,255,255,0.06)', borderRadius: 3 }} />
            ))}
          </div>
        </div>
      </div>
      <div style={{ padding: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 3 }}>
          <span style={{ fontSize: 13, fontWeight: 600 }}>{brand.name}</span>
          {brand.status === 'maintenance' && <span className="badge warning" style={{ fontSize: 9 }}>{t.tx('维护', 'Maint')}</span>}
        </div>
        <div className="text-mono text-faint" style={{ fontSize: 10 }}>{brand.domain}</div>
        <div style={{ display: 'flex', gap: 4, marginTop: 6, fontSize: 10, color: 'var(--text-muted)' }}>
          <span>{brand.market}</span><span>·</span><span>{brand.currency}</span>
          <span style={{ marginLeft: 'auto', color: 'var(--text)' }} className="tabular">{t.tx((brand.users / 10000).toFixed(1) + '万', (brand.users / 1000).toFixed(1) + 'k')}</span>
        </div>
      </div>
    </div>
  );
}

function BrandEditor({ brand, push, t }) {
  const [primary, setPrimary] = React.useState(brand.primary);
  const [bg, setBg] = React.useState(brand.bg);
  const [logo, setLogo] = React.useState(brand.logo);
  const [name, setName] = React.useState(brand.name);
  return (
    <div className="card">
      <div className="card-h">
        <h3>{t.tx('编辑品牌 · ', 'Edit Brand · ')}{brand.name}</h3>
        <div style={{ display: 'flex', gap: 6 }}>
          <button className="btn btn-sm">{t.tx('恢复默认', 'Reset')}</button>
          <button className="btn btn-sm">{t.tx('保存草稿', 'Save Draft')}</button>
          <button className="btn btn-pri btn-sm" onClick={() => push(brand.name + t.tx(' · 已发布更新', ' · update published'))}>{t.tx('发布到生产', 'Publish')}</button>
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: '320px 1fr 280px', gap: 16, padding: 16 }}>
        <div className="stack" style={{ gap: 14 }}>
          <Section title={t.tx('基本信息', 'Basics')}>
            <Field label={t.tx('品牌名称', 'Brand Name')}><input className="input" value={name} onChange={(e) => setName(e.target.value)} /></Field>
            <Field label={t.tx('域名', 'Domain')}><input className="input" defaultValue={brand.domain} /></Field>
            <Field label={t.tx('语言 / 币种', 'Language / Currency')}>
              <div style={{ display: 'flex', gap: 6 }}>
                <select className="select"><option>{brand.lang}</option></select>
                <select className="select"><option>{brand.currency}</option></select>
              </div>
            </Field>
          </Section>
          <Section title={t.tx('主题色', 'Theme Colors')}>
            <Field label={t.tx('主品牌色', 'Primary Color')}>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {['#FF3D7F','#6FE7FF','#F5C648','#FF6B4D','#9E5AFB','#2BD37B','#FF8A3D','#26A17B'].map(c => (
                  <button key={c} onClick={() => setPrimary(c)}
                          style={{ width: 28, height: 28, borderRadius: 6, background: c, border: primary === c ? '2px solid var(--text)' : '1px solid var(--border)', cursor: 'default' }} />
                ))}
              </div>
              <div className="text-mono text-faint" style={{ fontSize: 10, marginTop: 4 }}>{primary}</div>
            </Field>
            <Field label={t.tx('背景色', 'Background')}>
              <div style={{ display: 'flex', gap: 6 }}>
                {['#0A0A14','#0F1424','#1A1004','#1A0A0A','#0F0A24','#0A1A0F','#1A0F0A','#04140A','#FAFAF9','#F4F3F0'].map(c => (
                  <button key={c} onClick={() => setBg(c)}
                          style={{ width: 24, height: 24, borderRadius: 6, background: c, border: bg === c ? '2px solid var(--accent)' : '1px solid var(--border)', cursor: 'default' }} />
                ))}
              </div>
            </Field>
          </Section>
          <Section title="Logo">
            <Field label={t.tx('文字 Logo', 'Text Logo')}>
              <input className="input" value={logo} onChange={(e) => setLogo(e.target.value.slice(0, 2))} maxLength={2} />
            </Field>
            <Field label={t.tx('或上传图片', 'Or upload image')}>
              <button className="btn btn-sm" style={{ width: '100%' }}><Icons.upload /> {t.tx('上传 (200×200 PNG)', 'Upload (200×200 PNG)')}</button>
            </Field>
          </Section>
        </div>
        <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'flex-start' }}>
          <PhonePreview primary={primary} bg={bg} logo={logo} name={name} t={t} />
        </div>
        <div className="stack" style={{ gap: 14 }}>
          <Section title={t.tx('模块开关', 'Module Toggles')}>
            {[
              { name: t.tx('彩票投注', 'Lottery'),    on: true },
              { name: t.tx('短视频', 'Short Video'),     on: true },
              { name: t.tx('短剧 + 付费墙', 'Drama + Paywall'), on: true, sub: t.tx('前 3 集免费 · ¥1.99 解锁全集', 'First 3 eps free · ¥1.99 unlocks all') },
              { name: t.tx('长视频 + VIP', 'Long Video + VIP'), on: true },
              { name: t.tx('直播 + 跟单', 'Live + Copy-Bet'), on: true, hot: true },
              { name: t.tx('代理推广', 'Agent Referral'),    on: true },
              { name: t.tx('游戏厅', 'Game Hall'),     on: false },
              { name: t.tx('体育投注', 'Sportsbook'),    on: false, sub: t.tx('暂未开放', 'Not yet available') },
            ].map((m, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 0', borderBottom: i < 7 ? '1px solid var(--border)' : 'none' }}>
                <div>
                  <div style={{ fontSize: 12, fontWeight: 500, display: 'flex', alignItems: 'center', gap: 5 }}>
                    {m.name}
                    {m.hot && <span style={{ fontSize: 9, padding: '0 4px', background: 'var(--accent)', color: 'white', borderRadius: 3 }}>{t.tx('核心', 'Core')}</span>}
                  </div>
                  {m.sub && <div className="text-faint" style={{ fontSize: 10 }}>{m.sub}</div>}
                </div>
                <div style={{ width: 30, height: 16, borderRadius: 100, background: m.on ? 'var(--success)' : 'var(--bg-active)', position: 'relative' }}>
                  <div style={{ position: 'absolute', top: 2, left: m.on ? 16 : 2, width: 12, height: 12, borderRadius: '50%', background: 'white', boxShadow: 'var(--shadow-sm)' }} />
                </div>
              </div>
            ))}
          </Section>
        </div>
      </div>
    </div>
  );
}

function Section({ title, children }) {
  return (
    <div className="card" style={{ padding: 14 }}>
      <div className="text-muted" style={{ fontSize: 11, fontWeight: 600, letterSpacing: '.04em', textTransform: 'uppercase', marginBottom: 10 }}>{title}</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>{children}</div>
    </div>
  );
}

function Field({ label, children }) {
  return (
    <div className="field">
      <label>{label}</label>
      {children}
    </div>
  );
}

function PhonePreview({ primary, bg, logo, name, t }) {
  return (
    <div style={{
      width: 220, borderRadius: 28, background: '#18181B',
      padding: '12px 10px 26px',
      boxShadow: '0 16px 40px rgba(0,0,0,0.18), 0 0 0 1px rgba(0,0,0,0.08)',
      position: 'sticky', top: 12
    }}>
      <div style={{ width: 60, height: 16, background: '#0a0a0a', borderRadius: 8, margin: '0 auto 8px' }} />
      <div style={{ background: bg, borderRadius: 16, padding: 12, color: 'white', minHeight: 380, display: 'flex', flexDirection: 'column', gap: 10 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{ width: 24, height: 24, borderRadius: 6, background: primary, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 12, fontWeight: 700, color: 'white' }}>{logo}</div>
          <span style={{ fontSize: 11, fontWeight: 600 }}>{name}</span>
        </div>
        <div style={{ background: `linear-gradient(135deg, ${primary}, ${primary}aa)`, borderRadius: 10, padding: 10, color: 'white' }}>
          <div style={{ fontSize: 9, opacity: 0.85 }}>{t.tx('🔥 主推 · 时时彩', '🔥 Featured · Time-Lottery')}</div>
          <div style={{ fontSize: 14, fontWeight: 800, marginTop: 2 }}>02:48</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4 }}>
          {[t.tx('彩','Lot'), t.tx('短','Sht'), t.tx('播','Liv'), t.tx('剧','Drm'), t.tx('戏','Gms'), t.tx('充','Top'), t.tx('奖','Awd'), t.tx('客','CS')].map((c, i) => (
            <div key={i} style={{ aspectRatio: '1', borderRadius: 8, background: `${primary}${i === 0 ? '' : '33'}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, color: 'white', fontWeight: 600 }}>{c}</div>
          ))}
        </div>
      </div>
      <div className="text-faint" style={{ fontSize: 9.5, textAlign: 'center', marginTop: 8, fontFamily: 'var(--font-mono)' }}>{t.tx('实时预览 · ', 'Live preview · ')}{name}</div>
    </div>
  );
}

function BrandClonerModal({ onClose, push, t }) {
  const [step, setStep] = React.useState(1);
  const steps = [t.tx('基本信息', 'Basics'), t.tx('克隆源', 'Source'), t.tx('主题', 'Theme'), t.tx('模块', 'Modules'), t.tx('发布', 'Publish')];
  return (
    <Modal open={true} onClose={onClose} title={t.tx('克隆新品牌', 'Clone New Brand')} width={720}
           footer={<>
             <span style={{ marginRight: 'auto', fontSize: 11, color: 'var(--text-muted)' }}>{t.tx('步骤 ', 'Step ')}{step}/{steps.length}</span>
             {step > 1 && <button className="btn btn-sm" onClick={() => setStep(step - 1)}>{t.tx('上一步', 'Back')}</button>}
             {step < steps.length ? (
               <button className="btn btn-pri btn-sm" onClick={() => setStep(step + 1)}>{t.tx('下一步', 'Next')}</button>
             ) : (
               <button className="btn btn-pri btn-sm" onClick={() => { onClose(); push(t.tx('新品牌已创建 · 预计 8 分钟完成部署', 'New brand created · ~8 min to deploy')); }}>{t.tx('立即创建', 'Create Now')}</button>
             )}
           </>}>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 20 }}>
        {steps.map((s, i) => (
          <React.Fragment key={s}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <div style={{ width: 24, height: 24, borderRadius: '50%', background: i + 1 <= step ? 'var(--accent)' : 'var(--bg-inset)', color: i + 1 <= step ? 'white' : 'var(--text-muted)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 11, fontWeight: 700 }}>{i + 1}</div>
              <span style={{ fontSize: 11, fontWeight: i + 1 === step ? 700 : 500, color: i + 1 === step ? 'var(--text)' : 'var(--text-muted)' }}>{s}</span>
            </div>
            {i < steps.length - 1 && <div style={{ flex: 1, height: 1, background: i + 1 < step ? 'var(--accent)' : 'var(--border)' }} />}
          </React.Fragment>
        ))}
      </div>
      {step === 1 && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
          <Field label={t.tx('品牌名称', 'Brand Name')}><input className="input" placeholder={t.tx('例：Aurora Vietnam', 'e.g. Aurora Vietnam')} /></Field>
          <Field label={t.tx('品牌代号', 'Brand Code')}><input className="input" placeholder={t.tx('例：B-aurora-vn', 'e.g. B-aurora-vn')} /></Field>
          <Field label={t.tx('目标市场', 'Target Market')}><select className="select"><option>{t.tx('越南', 'Vietnam')}</option><option>{t.tx('泰国', 'Thailand')}</option><option>{t.tx('印尼', 'Indonesia')}</option><option>{t.tx('菲律宾', 'Philippines')}</option></select></Field>
          <Field label={t.tx('主语言', 'Primary Language')}><select className="select"><option>{t.tx('越南语', 'Vietnamese')}</option><option>{t.tx('泰语', 'Thai')}</option></select></Field>
        </div>
      )}
      {step >= 2 && (
        <div className="text-muted" style={{ fontSize: 12 }}>{t.tx('（演示步骤 ', '(Demo step ')}{step}{t.tx('）', ')')}</div>
      )}
    </Modal>
  );
}

Object.assign(window, { VideosScreen, LiveScreen, GamesScreen, CMSScreen, BrandMatrixScreen });
