/* ============================================================
   COURSEDY — Admin dashboard (real data)
   ============================================================ */

function AdminKpi({ label, value, icon }) {
  const Icon = ICON_BY_NAME(icon);
  return (
    <div className="card" style={{ padding: '18px 20px' }}>
      <div className="row" style={{ justifyContent: 'space-between' }}>
        <span className="center" style={{ width: 36, height: 36, borderRadius: 10, background: 'var(--brand-tint)', color: 'var(--brand)' }}><Icon size={18} /></span>
      </div>
      <div style={{ fontSize: 27, fontWeight: 800, letterSpacing: '-0.03em', marginTop: 16 }}>{value}</div>
      <div className="muted" style={{ fontSize: 13, marginTop: 2 }}>{label}</div>
    </div>
  );
}

function AdminBarChart({ data, title, count, accent }) {
  const maxBar = Math.max(1, ...data.map(d => d.count));
  return (
    <div className="card" style={{ padding: '22px 24px' }}>
      <div className="row" style={{ justifyContent: 'space-between', marginBottom: 4 }}>
        <div>
          <h3 style={{ fontSize: 16 }}>{title}</h3>
          <div className="row" style={{ gap: 8, marginTop: 4 }}>
            <span style={{ fontSize: 22, fontWeight: 800, letterSpacing: '-0.02em' }}>{count.toLocaleString()}</span>
            <span className="muted" style={{ fontSize: 12.5 }}>last 30 days</span>
          </div>
        </div>
      </div>
      <div className="row" style={{ alignItems: 'flex-end', gap: 'clamp(2px,1%,8px)', height: 160, marginTop: 18 }}>
        {data.map((d, i) => {
          const h = (d.count / maxBar) * 100;
          return (
            <div key={d.date} className="grow" title={`${d.date}: ${d.count}`} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, height: '100%', justifyContent: 'flex-end' }}>
              <div style={{ width: '100%', maxWidth: 22, height: `${Math.max(h, d.count > 0 ? 2 : 0)}%`, background: i === data.length - 1 ? (accent || 'var(--ai-grad)') : 'var(--brand-tint-2)', borderRadius: 5, transformOrigin: 'bottom', animation: `barGrow .6s ${i * 0.02}s var(--ease-out) both`, transition: 'background .2s' }}
                onMouseEnter={e => { if (i !== data.length - 1) e.currentTarget.style.background = 'var(--brand)'; }}
                onMouseLeave={e => { if (i !== data.length - 1) e.currentTarget.style.background = 'var(--brand-tint-2)'; }} />
            </div>
          );
        })}
      </div>
      <div className="row" style={{ justifyContent: 'space-between', marginTop: 10 }}>
        <span className="mono muted" style={{ fontSize: 10.5 }}>{data[0]?.date || ''}</span>
        <span className="mono muted" style={{ fontSize: 10.5 }}>{data[data.length - 1]?.date || ''}</span>
      </div>
    </div>
  );
}

function PlanPill({ plan, n }) {
  const color = plan === 'pro' ? { bg: 'var(--brand-tint)', fg: 'var(--brand-ink)' } : plan === 'teams' ? { bg: 'var(--amber-tint)', fg: '#8a560a' } : { bg: 'var(--surface-3)', fg: 'var(--muted)' };
  return (
    <span className="pill" style={{ background: color.bg, color: color.fg, gap: 8, padding: '6px 12px' }}>
      <b style={{ textTransform: 'capitalize' }}>{plan}</b>
      <span>{n.toLocaleString()}</span>
    </span>
  );
}

function AdminOverview({ stats }) {
  if (!stats) return <div className="muted" style={{ padding: 40 }}>Loading…</div>;
  const t = stats.totals;
  return (
    <div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 16 }}>
        <AdminKpi label="Total users" value={t.users.toLocaleString()} icon="Users" />
        <AdminKpi label="Courses generated" value={t.courses.toLocaleString()} icon="Book" />
        <AdminKpi label="Lessons written" value={t.lessons.toLocaleString()} icon="File" />
        <AdminKpi label="Exports" value={t.exports.toLocaleString()} icon="Download" />
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 16, marginTop: 16 }}>
        <AdminBarChart data={stats.generatedLast30Days} title="Courses generated" count={stats.generatedLast30Days.reduce((a, d) => a + d.count, 0)} />
        <div className="card" style={{ padding: '22px 24px' }}>
          <h3 style={{ fontSize: 16, marginBottom: 14 }}>Plan distribution</h3>
          <div className="row" style={{ gap: 8, flexWrap: 'wrap' }}>
            <PlanPill plan="trial" n={stats.byCoursePlan.trial} />
            <PlanPill plan="pro" n={stats.byCoursePlan.pro} />
            <PlanPill plan="teams" n={stats.byCoursePlan.teams} />
          </div>
          <div className="hairline" style={{ margin: '18px 0' }} />
          <h3 style={{ fontSize: 14, marginBottom: 10 }}>Recent exports by format</h3>
          {stats.recentExportsByFormat.length === 0 && <div className="muted" style={{ fontSize: 13 }}>No exports yet.</div>}
          <div className="row" style={{ gap: 8, flexWrap: 'wrap' }}>
            {stats.recentExportsByFormat.map(f => (
              <span key={f.format} className="chip" style={{ gap: 6 }}>
                <b style={{ textTransform: 'uppercase' }} className="mono">{f.format}</b>
                <span className="muted">{f.count}</span>
              </span>
            ))}
          </div>
        </div>
      </div>

      <div style={{ marginTop: 16 }}>
        <AdminBarChart data={stats.visitsLast30Days} title="Page visits" count={stats.visitsLast30Days.reduce((a, d) => a + d.count, 0)} />
      </div>

      <div className="card" style={{ marginTop: 16, overflow: 'hidden' }}>
        <div className="row" style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)' }}>
          <h3 style={{ fontSize: 16 }}>Top users by course count</h3>
        </div>
        <div style={{ overflowX: 'auto' }}>
          <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 480 }}>
            <thead>
              <tr style={{ fontSize: 11.5, color: 'var(--faint)', textTransform: 'uppercase', letterSpacing: '.06em' }} className="mono">
                <th style={{ textAlign: 'left', padding: '11px 22px', fontWeight: 500 }}>User</th>
                <th style={{ textAlign: 'left', padding: '11px 22px', fontWeight: 500 }}>Courses</th>
              </tr>
            </thead>
            <tbody>
              {stats.topUsers.length === 0 && (
                <tr><td colSpan={2} style={{ padding: '22px', textAlign: 'center' }} className="muted">No users yet.</td></tr>
              )}
              {stats.topUsers.map((u, i) => (
                <tr key={i} style={{ borderTop: '1px solid var(--line-2)' }}>
                  <td style={{ padding: '12px 22px' }}>
                    <div className="row" style={{ gap: 11 }}>
                      <Avatar name={u.name || u.email} size={32} />
                      <div>
                        <div style={{ fontSize: 13.8, fontWeight: 600 }}>{u.name || u.email}</div>
                        <div className="muted" style={{ fontSize: 12 }}>{u.email}</div>
                      </div>
                    </div>
                  </td>
                  <td style={{ padding: '12px 22px', fontSize: 13.5, fontWeight: 600 }}>{u.coursesCount}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function AdminUsersTab({ currentUserId }) {
  const [users, setUsers] = useState(null);
  const [error, setError] = useState(null);
  const [busyId, setBusyId] = useState(null);
  const load = async () => {
    try {
      const d = await window.API.adminUsers(200);
      setUsers(d.users || []);
    } catch (e) {
      setError(e?.message || 'Failed to load users');
    }
  };
  useEffect(() => { load(); /* eslint-disable-next-line */ }, []);

  const toggle = async (u) => {
    setBusyId(u.id);
    setError(null);
    try {
      await window.API.adminPromoteUser(u.id, !u.isSuperAdmin);
      await load();
    } catch (e) {
      setError(e?.message || 'Update failed');
    } finally {
      setBusyId(null);
    }
  };

  return (
    <div className="card" style={{ overflow: 'hidden' }}>
      <div className="row" style={{ padding: '16px 22px', borderBottom: '1px solid var(--line)' }}>
        <h3 style={{ fontSize: 16 }}>Users</h3>
        <span className="pill" style={{ height: 24, marginLeft: 10 }}>{users ? users.length : '…'}</span>
        <div className="grow" />
      </div>
      {error && <div className="card fade-in" style={{ margin: 16, padding: '10px 14px', background: 'var(--rose-tint, #fde9ee)', color: '#9c1f3c', borderRadius: 12, fontSize: 13.5 }}>{error}</div>}
      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 820 }}>
          <thead>
            <tr style={{ fontSize: 11.5, color: 'var(--faint)', textTransform: 'uppercase', letterSpacing: '.06em' }} className="mono">
              {['User', 'Plan', 'Courses', 'Super admin', 'Joined', ''].map((h, i) => (
                <th key={i} style={{ textAlign: 'left', padding: '11px 22px', fontWeight: 500 }}>{h}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {!users && (
              <tr><td colSpan={6} style={{ padding: 22 }} className="muted">Loading…</td></tr>
            )}
            {users && users.length === 0 && (
              <tr><td colSpan={6} style={{ padding: 22, textAlign: 'center' }} className="muted">No users.</td></tr>
            )}
            {users && users.map((u) => (
              <tr key={u.id} style={{ borderTop: '1px solid var(--line-2)' }}>
                <td style={{ padding: '12px 22px' }}>
                  <div className="row" style={{ gap: 11 }}>
                    <Avatar name={u.name || u.email} size={34} />
                    <div>
                      <div style={{ fontSize: 13.8, fontWeight: 600 }}>{u.name || u.email}</div>
                      <div className="muted" style={{ fontSize: 12 }}>{u.email}</div>
                    </div>
                  </div>
                </td>
                <td style={{ padding: '12px 22px' }}>
                  <span className="badge" style={{
                    background: u.plan === 'pro' ? 'var(--brand-tint)' : u.plan === 'teams' ? 'var(--amber-tint)' : 'var(--surface-3)',
                    color: u.plan === 'pro' ? 'var(--brand-ink)' : u.plan === 'teams' ? '#8a560a' : 'var(--muted)',
                    textTransform: 'capitalize',
                  }}>{u.plan}</span>
                </td>
                <td style={{ padding: '12px 22px', fontSize: 13.5, fontWeight: 600 }}>{u.courseCount}</td>
                <td style={{ padding: '12px 22px' }}>
                  {u.isSuperAdmin ? <span className="badge badge-brand" style={{ background: 'var(--brand)', color: '#fff' }}>Super admin</span> : <span className="muted" style={{ fontSize: 13 }}>—</span>}
                </td>
                <td style={{ padding: '12px 22px', fontSize: 13, color: 'var(--muted)' }}>{new Date(u.createdAt).toLocaleDateString()}</td>
                <td style={{ padding: '12px 22px', textAlign: 'right' }}>
                  <button
                    className="btn btn-sm btn-outline"
                    disabled={busyId === u.id}
                    onClick={() => toggle(u)}
                    style={{ minWidth: 130 }}
                  >
                    {busyId === u.id ? 'Saving…' : u.isSuperAdmin ? 'Demote' : 'Promote to admin'}
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

const STRIPE_FIELDS = [
  { key: 'STRIPE_PUBLISHABLE_KEY', label: 'Publishable key', secret: false, help: 'Starts with pk_live_ or pk_test_. Find it in Stripe Dashboard → Developers → API keys.' },
  { key: 'STRIPE_SECRET_KEY', label: 'Secret key', secret: true, help: 'Starts with sk_live_ or sk_test_. Keep this private. Same page in Stripe → API keys.' },
  { key: 'STRIPE_WEBHOOK_SECRET', label: 'Webhook signing secret', secret: true, help: 'Starts with whsec_. Create a webhook endpoint pointing at https://coursedy.com/api/billing/webhook then copy its signing secret.' },
  { key: 'STRIPE_PRO_PRICE_ID', label: 'Pro plan price ID', secret: false, help: 'Starts with price_. In Stripe → Products → your Pro plan → Pricing section.' },
  { key: 'STRIPE_TEAMS_PRICE_ID', label: 'Teams plan price ID', secret: false, help: 'Starts with price_. In Stripe → Products → your Teams plan → Pricing section.' },
];

function AdminSettingsTab() {
  const [settings, setSettings] = useState(null);
  const [edits, setEdits] = useState({});
  const [focused, setFocused] = useState({});
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState(null);
  const [success, setSuccess] = useState(null);

  const load = async () => {
    try {
      const d = await window.API.adminGetSettings();
      setSettings(d.settings || {});
    } catch (e) {
      setError(e?.message || 'Failed to load settings');
    }
  };
  useEffect(() => { load(); /* eslint-disable-next-line */ }, []);

  const save = async (e) => {
    e?.preventDefault?.();
    if (Object.keys(edits).length === 0) {
      setSuccess('No changes to save.');
      return;
    }
    setBusy(true);
    setError(null);
    setSuccess(null);
    try {
      const d = await window.API.adminSetSettings(edits);
      setSettings(d.settings || {});
      setEdits({});
      setFocused({});
      setSuccess('Settings saved.');
    } catch (err) {
      setError(err?.message || 'Save failed');
    } finally {
      setBusy(false);
    }
  };

  const onFocus = (key, secret) => {
    if (!secret) return;
    if (focused[key]) return;
    setFocused({ ...focused, [key]: true });
    setEdits({ ...edits, [key]: edits[key] ?? '' });
  };

  const onChange = (key, value) => {
    setEdits({ ...edits, [key]: value });
  };

  const valueFor = (f) => {
    if (Object.prototype.hasOwnProperty.call(edits, f.key)) return edits[f.key];
    if (f.secret && !focused[f.key]) return settings?.[f.key] || '';
    return settings?.[f.key] || '';
  };

  const stripeOk = !!(settings && (edits.STRIPE_SECRET_KEY || (settings.STRIPE_SECRET_KEY && !Object.prototype.hasOwnProperty.call(edits, 'STRIPE_SECRET_KEY'))) && (settings.STRIPE_PRO_PRICE_ID || edits.STRIPE_PRO_PRICE_ID));

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div className="card" style={{ padding: '22px 24px' }}>
        <h3 style={{ fontSize: 16, marginBottom: 6 }}>Stripe configuration</h3>
        <div className="muted" style={{ fontSize: 13, marginBottom: 18 }}>
          Stored in the database — no env vars required. Secrets are masked on read; click the field to enter a new value. Leave a field blank and save to clear it.
        </div>
        {error && <div className="card fade-in" style={{ padding: '10px 14px', marginBottom: 12, background: 'var(--rose-tint, #fde9ee)', color: '#9c1f3c', borderRadius: 12, fontSize: 13.5 }}>{error}</div>}
        {success && <div className="card fade-in" style={{ padding: '10px 14px', marginBottom: 12, background: 'var(--green-tint)', color: '#0e6b47', borderRadius: 12, fontSize: 13.5 }}>{success}</div>}
        <form onSubmit={save} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {STRIPE_FIELDS.map(f => (
            <div key={f.key}>
              <label style={{ display: 'block', fontSize: 13, fontWeight: 600, marginBottom: 6 }}>{f.label}</label>
              <input
                className="field mono"
                style={{ width: '100%', fontSize: 13 }}
                type="text"
                autoComplete="off"
                spellCheck={false}
                placeholder={f.secret ? 'Click to enter a new value' : ''}
                value={valueFor(f)}
                onFocus={() => onFocus(f.key, f.secret)}
                onChange={(e) => onChange(f.key, e.target.value)}
              />
              <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>{f.help}</div>
            </div>
          ))}
          <div className="row" style={{ gap: 10, marginTop: 6 }}>
            <Btn variant="primary" disabled={busy} onClick={save}>{busy ? 'Saving…' : 'Save settings'}</Btn>
            <Btn variant="outline" disabled={busy} onClick={(e) => { e.preventDefault(); setEdits({}); setFocused({}); setSuccess(null); setError(null); }}>Reset</Btn>
          </div>
        </form>
      </div>

      <div className="card" style={{ padding: '18px 22px' }}>
        <div className="row" style={{ gap: 12 }}>
          {stripeOk ? (
            <>
              <span className="center" style={{ width: 36, height: 36, borderRadius: 10, background: 'var(--green-tint)', color: '#0e6b47' }}><Check size={16} /></span>
              <div>
                <div style={{ fontSize: 14, fontWeight: 600 }}>Stripe is configured</div>
                <div className="muted" style={{ fontSize: 13 }}>Checkout will create real subscription sessions.</div>
              </div>
            </>
          ) : (
            <>
              <span className="center" style={{ width: 36, height: 36, borderRadius: 10, background: 'var(--amber-tint)', color: '#8a560a' }}><Shield size={16} /></span>
              <div>
                <div style={{ fontSize: 14, fontWeight: 600 }}>Stripe is not fully configured</div>
                <div className="muted" style={{ fontSize: 13 }}>Set at least the secret key and Pro price ID. Until then, upgrades fall back to a direct plan flip for testing.</div>
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

function Admin({ go, user }) {
  const [tab, setTab] = useState('overview');
  const [stats, setStats] = useState(null);
  const [error, setError] = useState(null);
  useEffect(() => {
    let cancelled = false;
    window.API.adminStats().then(d => { if (!cancelled) setStats(d); }).catch(e => { if (!cancelled) setError(e?.message || 'Failed to load stats'); });
    return () => { cancelled = true; };
  }, []);

  const tabs = [
    { id: 'overview', label: 'Overview' },
    { id: 'users', label: 'Users' },
    { id: 'settings', label: 'Settings' },
    { id: 'docs', label: 'Docs' },
  ];

  return (
    <div data-app-scroll className="scroll" style={{ height: '100vh', overflowY: 'auto' }} data-screen-label="Admin">
      <Topbar go={go} title="Admin" sub="Platform overview" user={user}>
        <div className="row card" style={{ padding: '0 3px', height: 34 }}>
          {tabs.map(t => (
            <button key={t.id} className="btn btn-sm" style={{ height: 28, padding: '0 13px', background: tab === t.id ? 'var(--surface-2)' : 'transparent', color: tab === t.id ? 'var(--ink)' : 'var(--muted)' }} onClick={() => setTab(t.id)}>{t.label}</button>
          ))}
        </div>
      </Topbar>

      <div style={{ maxWidth: 1180, margin: '0 auto', padding: '26px 32px 64px' }}>
        {error && <div className="card fade-in" style={{ padding: '12px 16px', marginBottom: 16, background: 'var(--rose-tint, #fde9ee)', color: '#9c1f3c', borderRadius: 12, fontSize: 14 }}>{error}</div>}
        {tab === 'overview' && <AdminOverview stats={stats} />}
        {tab === 'users' && <AdminUsersTab currentUserId={user?.id} />}
        {tab === 'settings' && <AdminSettingsTab />}
        {tab === 'docs' && <AdminDocsTab />}
      </div>
    </div>
  );
}

/* ============================================================
   AdminDocsTab — owner-facing documentation
   ============================================================ */
function DocSection({ icon, eyebrow, title, children }) {
  return (
    <div className="card" style={{ padding: '24px 26px', marginBottom: 16 }}>
      <div className="row" style={{ gap: 12, alignItems: 'flex-start', marginBottom: 14 }}>
        {icon && (
          <span className="center" style={{ width: 38, height: 38, borderRadius: 11, background: 'var(--brand-tint)', color: 'var(--brand)', flexShrink: 0 }}>
            {icon}
          </span>
        )}
        <div>
          {eyebrow && <div className="mono" style={{ fontSize: 11, letterSpacing: '.12em', textTransform: 'uppercase', color: 'var(--faint)', marginBottom: 4 }}>{eyebrow}</div>}
          <div style={{ fontSize: 17, fontWeight: 700, letterSpacing: '-0.01em' }}>{title}</div>
        </div>
      </div>
      <div style={{ fontSize: 14, lineHeight: 1.65, color: 'var(--ink-2)' }}>{children}</div>
    </div>
  );
}

function CodeChip({ children }) {
  return (
    <code className="mono" style={{ background: 'var(--surface-2)', padding: '2px 7px', borderRadius: 4, fontSize: 12.5, color: 'var(--ink)' }}>{children}</code>
  );
}

function AdminDocsTab() {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 0, maxWidth: 880 }}>
      <DocSection
        icon={<Shield size={18} />}
        eyebrow="Super admin"
        title="Who can see this panel"
      >
        <p style={{ margin: 0 }}>
          The Admin section is only visible to users whose <CodeChip>isSuperAdmin</CodeChip> flag is true on the User row.
          The owner account is <CodeChip>admin@coursedy.com</CodeChip> with password <CodeChip>admin123</CodeChip>.
          Every other user — including <CodeChip>demo@coursedy.com</CodeChip> — is a regular account and does not see the Admin item in the sidebar.
        </p>
        <p style={{ margin: '12px 0 0' }}>
          To promote or demote a user, open the <b>Users</b> tab and click the Promote / Demote button on that row.
          The server refuses to remove the last super admin, so there is always at least one owner account.
        </p>
      </DocSection>

      <DocSection
        icon={<Dollar size={18} />}
        eyebrow="Stripe billing"
        title="Connect Stripe to enable real upgrades"
      >
        <p style={{ margin: 0 }}>
          Until Stripe is configured, the Upgrade button on <CodeChip>/upgrade</CodeChip> falls back to instant-flip mode so you can demo the flow without payments.
          To activate real Stripe Checkout, paste five values into the <b>Settings</b> tab:
        </p>
        <ol style={{ margin: '12px 0 0 20px', padding: 0 }}>
          <li style={{ marginBottom: 6 }}><b>Publishable key</b> — <CodeChip>pk_live_…</CodeChip> or <CodeChip>pk_test_…</CodeChip> from Stripe Dashboard → Developers → API keys.</li>
          <li style={{ marginBottom: 6 }}><b>Secret key</b> — <CodeChip>sk_live_…</CodeChip> or <CodeChip>sk_test_…</CodeChip> from the same page. Treat like a password — it is masked after saving.</li>
          <li style={{ marginBottom: 6 }}><b>Webhook signing secret</b> — <CodeChip>whsec_…</CodeChip>. See the Webhooks section below for how to mint this.</li>
          <li style={{ marginBottom: 6 }}><b>Pro price ID</b> — <CodeChip>price_…</CodeChip> for the recurring Pro subscription product you create in Stripe → Products.</li>
          <li><b>Teams price ID</b> — <CodeChip>price_…</CodeChip> for the Teams subscription product.</li>
        </ol>
        <p style={{ margin: '14px 0 0' }}>
          When the Secret key + Pro price ID are saved, the <b>Stripe status</b> card on the Settings tab turns green and the Upgrade flow starts using Stripe Checkout.
          Secrets are stored in the <CodeChip>Setting</CodeChip> table on Neon and never leave your database.
        </p>
      </DocSection>

      <DocSection
        icon={<Zap size={18} />}
        eyebrow="Stripe webhook"
        title="Point Stripe at your live site"
      >
        <p style={{ margin: 0 }}>
          The webhook is what flips a user's plan from <CodeChip>trial</CodeChip> to <CodeChip>pro</CodeChip> after a successful payment, and back to <CodeChip>trial</CodeChip> when the subscription is cancelled.
        </p>
        <ol style={{ margin: '12px 0 0 20px', padding: 0 }}>
          <li style={{ marginBottom: 6 }}>Open Stripe Dashboard → Developers → Webhooks → <b>Add endpoint</b>.</li>
          <li style={{ marginBottom: 6 }}>Endpoint URL: <CodeChip>https://www.coursedy.com/api/billing/webhook</CodeChip></li>
          <li style={{ marginBottom: 6 }}>Subscribe to these two events:
            <div style={{ marginTop: 6 }}>
              <CodeChip>checkout.session.completed</CodeChip>{' '}
              <CodeChip>customer.subscription.deleted</CodeChip>
            </div>
          </li>
          <li style={{ marginBottom: 6 }}>Stripe will show a <CodeChip>whsec_…</CodeChip> signing secret. Copy it into Settings → Webhook signing secret.</li>
        </ol>
        <p style={{ margin: '14px 0 0' }}>
          The webhook endpoint verifies every request signature before touching the database — calls with an invalid signature get a 400 and no row is updated.
        </p>
      </DocSection>

      <DocSection
        icon={<Layers size={18} />}
        eyebrow="Plan limits"
        title="Free tier caps and how to change them"
      >
        <p style={{ margin: 0 }}>
          The Free tier is capped at <b>1 course</b>, <b>1 module per course</b>, and <b>3 lessons per module</b>.
          Pro and Teams are unlimited.
          When a free user hits a cap the API returns <CodeChip>402 PLAN_LIMIT_EXCEEDED</CodeChip>, the SPA detects this and shows an Upgrade dialog that routes to <CodeChip>/upgrade</CodeChip>.
        </p>
        <p style={{ margin: '12px 0 0' }}>
          To change the limits, edit <CodeChip>src/lib/plan.ts</CodeChip> in the codebase and redeploy. Both server enforcement and the UI text update from that single file.
        </p>
      </DocSection>

      <DocSection
        icon={<Cpu size={18} />}
        eyebrow="API keys not in this panel"
        title="DeepSeek and Neon live in environment variables"
      >
        <p style={{ margin: 0 }}>
          Two infrastructure-level secrets — the DeepSeek API key (powers generation, chat, regenerate) and the Neon DATABASE_URL (Postgres connection) — are intentionally <b>not exposed</b> in this Settings tab.
          They are set as environment variables on Vercel and rotated from there:
        </p>
        <ul style={{ margin: '12px 0 0 20px', padding: 0 }}>
          <li style={{ marginBottom: 6 }}>Vercel Dashboard → coursedy project → Settings → Environment Variables.</li>
          <li style={{ marginBottom: 6 }}>Keys currently configured: <CodeChip>DATABASE_URL</CodeChip>, <CodeChip>DIRECT_URL</CodeChip>, <CodeChip>DEEPSEEK_API_KEY</CodeChip>, <CodeChip>SERPER_API_KEY</CodeChip>, <CodeChip>JWT_SECRET</CodeChip>.</li>
          <li>Any change requires a redeploy to take effect.</li>
        </ul>
        <p style={{ margin: '12px 0 0' }}>
          This separation is deliberate — Stripe is a configurable business setting; DeepSeek and Neon are infrastructure that should rotate through proper deployment.
        </p>
      </DocSection>

      <DocSection
        icon={<Trend size={18} />}
        eyebrow="Stats and visits"
        title="What the Overview shows"
      >
        <p style={{ margin: 0 }}>
          Every number in the Overview tab is live from Neon — there is no mock or seeded data.
        </p>
        <ul style={{ margin: '12px 0 0 20px', padding: 0 }}>
          <li style={{ marginBottom: 6 }}><b>Users / Courses / Lessons / Exports</b> — raw counts from <CodeChip>User</CodeChip>, <CodeChip>Course</CodeChip>, <CodeChip>Lesson</CodeChip>, and <CodeChip>ExportRecord</CodeChip>.</li>
          <li style={{ marginBottom: 6 }}><b>Daily bar charts</b> — Postgres <CodeChip>date_trunc('day', "createdAt")</CodeChip> grouped over the last 30 days.</li>
          <li style={{ marginBottom: 6 }}><b>Visits</b> — every route change in the SPA fires <CodeChip>POST /api/track/visit</CodeChip>; deduped per session via <CodeChip>window.__lastVisitPath</CodeChip>.</li>
          <li style={{ marginBottom: 6 }}><b>Top users</b> — Prisma <CodeChip>_count.courses</CodeChip> ordered descending, top 10.</li>
          <li><b>Exports by format</b> — last 30 days, grouped by format string (docx, pdf, scorm, etc).</li>
        </ul>
      </DocSection>

      <DocSection
        icon={<Library size={18} />}
        eyebrow="Course content"
        title="Eight export formats and how they map to platforms"
      >
        <p style={{ margin: 0 }}>
          Every generated course can be exported via the dropdown on the Library card or the Export button inside the editor.
        </p>
        <ul style={{ margin: '12px 0 0 20px', padding: 0 }}>
          <li style={{ marginBottom: 6 }}><b>DOCX, PDF, HTML, Markdown</b> — document-style exports for review, blog posts, or print.</li>
          <li style={{ marginBottom: 6 }}><b>ePub</b> — valid EPUB 3 ZIP for ebook stores and reading apps.</li>
          <li style={{ marginBottom: 6 }}><b>JSON</b> — full course tree for custom integrations or backup.</li>
          <li style={{ marginBottom: 6 }}><b>Quiz CSV</b> — Udemy practice-test format for bulk quiz import.</li>
          <li><b>SCORM 1.2</b> — LMS-ready ZIP with <CodeChip>imsmanifest.xml</CodeChip>, per-lesson HTML, and a SCORM API stub for completion tracking. Drop into Moodle, Canvas, LearnUpon, TalentLMS, or any SCORM 1.2 player.</li>
        </ul>
      </DocSection>

      <DocSection
        icon={<File size={18} />}
        eyebrow="Deployment"
        title="Hosting and auto-deploy"
      >
        <p style={{ margin: 0 }}>
          The site lives on Vercel at <CodeChip>coursedy.com</CodeChip> (apex and <CodeChip>www</CodeChip>).
          Every push to the <CodeChip>master</CodeChip> branch on GitHub <CodeChip>vinaysolapurkar/course-creator-ai</CodeChip> triggers an automatic production deploy.
          Build runs <CodeChip>prisma generate && next build</CodeChip>; Prisma client is also regenerated on every <CodeChip>npm install</CodeChip> via the <CodeChip>postinstall</CodeChip> script.
        </p>
        <p style={{ margin: '14px 0 0' }}>
          To roll back, use Vercel's deployment history — every prior deploy can be promoted back to production with one click.
        </p>
      </DocSection>

      <DocSection
        icon={<Edit size={18} />}
        eyebrow="Audit log"
        title="Edit history"
      >
        <p style={{ margin: 0 }}>
          Every PATCH to a course, module, lesson, or quiz question writes a <CodeChip>CourseEdit</CodeChip> row with the field changed, before-value, after-value, timestamp, and the user who made the change.
          Inside the editor, the clock icon next to the Export button opens a side drawer showing the timeline for the current course.
          The audit log is append-only — no UI to delete rows.
        </p>
      </DocSection>

      <div className="muted" style={{ textAlign: 'center', fontSize: 12, padding: '20px 0' }}>
        Coursedy admin documentation · keep these notes private to super admin accounts
      </div>
    </div>
  );
}

Object.assign(window, { Admin });
