// app.jsx — Root component, wires shell + screens + tweaks

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "lang": "cn",
  "font": "geist",
  "density": "regular"
}/*EDITMODE-END*/;

const ADMIN_TOKEN_KEY = 'nebula.admin.token';
const ADMIN_USER_KEY  = 'nebula.admin.user';

function AdminLoginGate({ onLogin }) {
  const lang = (() => { try { return JSON.parse(localStorage.getItem('nebula.tweaks') || '{}').lang || 'cn'; } catch { return 'cn'; } })();
  const tx = (zh, en) => lang === 'cn' ? zh : (en || zh);
  const [username, setUsername] = React.useState('admin');
  const [password, setPassword] = React.useState('');
  const [err, setErr] = React.useState(null);
  const [submitting, setSubmitting] = React.useState(false);
  const submit = async (e) => {
    e?.preventDefault();
    setSubmitting(true); setErr(null);
    try {
      const r = await fetch('/api/auth/admin-login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password }),
      });
      if (!r.ok) {
        const j = await r.json().catch(() => ({}));
        throw new Error(j.error || ('HTTP ' + r.status));
      }
      const j = await r.json();
      localStorage.setItem(ADMIN_TOKEN_KEY, j.token);
      localStorage.setItem(ADMIN_USER_KEY, JSON.stringify(j.user));
      onLogin(j.user);
    } catch (e) { setErr(String(e.message || e)); }
    finally { setSubmitting(false); }
  };
  return (
    <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'radial-gradient(circle at 30% 0%, oklch(0.25 0.04 264) 0%, oklch(0.16 0.02 270) 60%)' }}>
      <form onSubmit={submit} style={{ width: 360, padding: 28, background: 'var(--bg-card)', borderRadius: 14, boxShadow: '0 24px 80px rgba(0,0,0,0.4)', display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div>
          <div style={{ fontSize: 20, fontWeight: 700 }}>Nebula Console</div>
          <div className="text-muted" style={{ fontSize: 12, marginTop: 4 }}>{tx('运营 / 风控 / 财务 登录', 'Operations / Risk / Finance Sign in')}</div>
        </div>
        <Field label={tx('账号', 'Account')}>
          <input className="input" value={username} onChange={(e) => setUsername(e.target.value)} autoFocus />
        </Field>
        <Field label={tx('密码', 'Password')}>
          <input className="input" type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
        </Field>
        {err && <div style={{ fontSize: 11, color: 'var(--danger)' }}>{err}</div>}
        <button type="submit" className="btn btn-pri" disabled={submitting}>{submitting ? tx('登录中…', 'Signing in…') : tx('登录', 'Sign in')}</button>
        <div className="text-faint" style={{ fontSize: 10, lineHeight: 1.5 }}>
          {tx('演示账号：', 'Demo: ')}<br />admin / admin123{tx('（运营）', ' (Operations)')}<br />finance / finance123{tx('（财务）', ' (Finance)')}
        </div>
      </form>
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [page, setPage] = React.useState('dashboard');
  const [pushToast, toastNode] = useToasts();
  const [sideOpen, setSideOpen] = React.useState(false);
  const tr = useI18n(t.lang);

  const [user, setUser] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem(ADMIN_USER_KEY) || 'null'); } catch (e) { return null; }
  });
  // On boot, validate token; if /me returns 401, log out.
  React.useEffect(() => {
    const token = localStorage.getItem(ADMIN_TOKEN_KEY);
    if (!token || !user) return;
    fetch('/api/auth/me', { headers: { Authorization: 'Bearer ' + token } })
      .then(r => { if (r.status === 401) { localStorage.removeItem(ADMIN_TOKEN_KEY); localStorage.removeItem(ADMIN_USER_KEY); setUser(null); } });
  }, [user]);

  // Frontend error capture → /api/admin/errors/report.
  // Debounced: same message hash is never sent twice within 60s, to avoid
  // floods from recurring effects or render loops.
  React.useEffect(() => {
    const recent = new Map(); // hash → expiresAt
    const hash = (s) => {
      let h = 0;
      for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;
      return String(h);
    };
    const report = (payload) => {
      const k = hash((payload.message || '') + '|' + (payload.stack || '').slice(0, 200));
      const now = Date.now();
      const exp = recent.get(k);
      if (exp && exp > now) return;
      recent.set(k, now + 60_000);
      try {
        fetch('/api/admin/errors/report', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            message:  String(payload.message || '').slice(0, 2000),
            stack:    String(payload.stack || '').slice(0, 8000),
            url:      location.href,
            severity: payload.severity || 'error',
            source:   'frontend',
          }),
          keepalive: true,
        }).catch(() => {});
      } catch {}
    };
    const onErr = (event) => {
      const msg = (event && (event.message || (event.error && event.error.message))) || 'unknown error';
      const stack = event && event.error && event.error.stack ? event.error.stack : '';
      report({ message: msg, stack, severity: 'error' });
    };
    const onRej = (event) => {
      const reason = event && event.reason;
      const msg = reason && (reason.message || String(reason)) || 'unhandled rejection';
      const stack = reason && reason.stack ? reason.stack : '';
      report({ message: 'UnhandledRejection: ' + msg, stack, severity: 'error' });
    };
    window.addEventListener('error', onErr);
    window.addEventListener('unhandledrejection', onRej);
    return () => {
      window.removeEventListener('error', onErr);
      window.removeEventListener('unhandledrejection', onRej);
    };
  }, []);

  const logout = () => {
    const token = localStorage.getItem(ADMIN_TOKEN_KEY);
    if (token) fetch('/api/auth/logout', { method: 'POST', headers: { Authorization: 'Bearer ' + token } }).catch(() => {});
    localStorage.removeItem(ADMIN_TOKEN_KEY);
    localStorage.removeItem(ADMIN_USER_KEY);
    setUser(null);
  };

  if (!user) return <AdminLoginGate onLogin={setUser} />;

  const Screen = {
    dashboard: DashboardScreen,
    tenants:   TenantsScreen,
    sites:     SitesScreen,
    builder:   BuilderScreen,
    users:     UserScreen,
    levels:    LevelsScreen,
    agents:    AgentsScreen,
    kyc:       KYCScreen,
    marketing: MarketingScreen,
    finance:   FinanceScreen,
    payments:  PaymentsScreen,
    games:     GamesScreen,
    videos:    VideosScreen,
    live:      LiveScreen,
    cms:       CMSScreen,
    brands:    BrandMatrixScreen,
    bets:      BetsScreen,
    pool:      PoolControlScreen,
    draws:     DrawsScreen,
    trends:    TrendDetectionScreen,
    winback:   WinbackScreen,
    risk:      RiskScreen,
    analytics: AnalyticsScreen,
    approvals: ApprovalsScreen,
    audit:     AuditScreen,
    support:   SupportScreen,
    broadcast: BroadcastScreen,
    announce:  AnnouncementsScreen,
    abtest:    ABTestScreen,
    theme:     ThemeScreen,
    templates: TemplatesScreen,
    errors:    ErrorsScreen,
    bi:        BIScreen,
    ops:       OpsScreen,
  }[page] || DashboardScreen;

  return (
    <div className={`app font-${t.font} density-${t.density}`}>
      <Sidebar active={page} onNav={setPage} t={tr}
               open={sideOpen} onClose={() => setSideOpen(false)} />
      <Topbar active={page} t={tr} lang={t.lang} onLang={(v) => setTweak('lang', v)}
              onMenuToggle={() => setSideOpen(o => !o)} />
      <main className="app-main" key={page}>
        <Screen t={tr} push={pushToast} />
      </main>
      {toastNode}

      <TweaksPanel title="Tweaks">
        <TweakSection label={`${tr.tx('已登录','Signed in')} · ${user.name}`} />
        <button className="btn btn-sm" onClick={logout} style={{ width: '100%', fontSize: 11 }}>{tr.tx('登出','Sign out')}</button>
        <TweakSection label="Language" />
        <TweakRadio label={tr.tx('界面语言 / Language','Language')} value={t.lang}
                    options={[{ value: 'cn', label: '中文' }, { value: 'en', label: 'English' }]}
                    onChange={(v) => setTweak('lang', v)} />
        <TweakSection label="Typography" />
        <TweakRadio label="Font" value={t.font}
                    options={[
                      { value: 'geist', label: 'Geist' },
                      { value: 'plex',  label: 'Plex' },
                      { value: 'mona',  label: 'Mona' },
                      { value: 'noto',  label: 'Noto' },
                    ]}
                    onChange={(v) => setTweak('font', v)} />
        <TweakSection label="Layout" />
        <TweakRadio label={tr.tx('信息密度 / Density','Density')} value={t.density}
                    options={[
                      { value: 'compact', label: tr.tx('紧凑','Compact') },
                      { value: 'regular', label: tr.tx('标准','Regular') },
                      { value: 'cozy',    label: tr.tx('舒适','Cozy') },
                    ]}
                    onChange={(v) => setTweak('density', v)} />
        <TweakSection label="Navigate" />
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, padding: '2px 0' }}>
          {[
            ['dashboard', tr.tx('总控台','Dashboard')],
            ['users', tr.tx('会员','Users')],
            ['marketing', tr.tx('活动 ★','Campaigns ★')],
            ['finance', tr.tx('账本','Ledger')],
            ['payments', tr.tx('支付路由','Payment Routing')],
            ['builder', tr.tx('装修','Builder')],
            ['pool', tr.tx('控盘 ★','Pool ★')],
            ['draws', tr.tx('开奖管理 ★','Draws ★')],
            ['trends', tr.tx('走势/长龙 ★','Trends ★')],
            ['winback', tr.tx('追损 ★','Winback ★')],
            ['risk', tr.tx('风控','Risk')],
            ['approvals', tr.tx('审批','Approvals')],
            ['audit', tr.tx('审计','Audit')],
            ['support', tr.tx('客服','Support')],
            ['broadcast', tr.tx('广播','Broadcast')],
          ].map(([id, label]) => (
            <button key={id} className={'chip ' + (page === id ? 'active' : '')} onClick={() => setPage(id)} style={{ fontSize: 10 }}>
              {label}
            </button>
          ))}
        </div>
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
