/* Adeyy admin — A1 Vendors, A2 Reserved slugs, A3 Platform health.
   Internal ProDesk staff tool. Same system, ink chrome, no teal wayfinding.
   Live API version. */

/* ── Minimal tRPC client ─────────────────────────────────────────────────── */
async function adminTrpc(path, input, method) {
  method = method || 'query';
  var url = '/api/trpc/' + path;
  var opts = { headers: { 'Content-Type': 'application/json' }, credentials: 'include' };
  if (method === 'query') {
    url += '?input=' + encodeURIComponent(JSON.stringify({ json: input || {} }));
    opts.method = 'GET';
  } else {
    opts.method = 'POST';
    opts.body = JSON.stringify({ json: input || {} });
  }
  var res = await fetch(url, opts);
  var json = await res.json();
  if (json.error) {
    var msg = (json.error.json && json.error.json.message) || json.error.message || 'API error';
    throw new Error(msg);
  }
  return json.result && json.result.data && json.result.data.json !== undefined
    ? json.result.data.json
    : (json.result && json.result.data);
}

function AdminApp() {
  const [tab, setTab] = React.useState('vendors');
  const [toasts, setToasts] = React.useState([]);
  const [user, setUser] = React.useState(null);
  const [authChecked, setAuthChecked] = React.useState(false);

  const toast = (msg) => {
    const id = Date.now();
    setToasts((t) => t.concat([{ id, msg }]));
    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 2600);
  };

  /* Auth check — must be admin role */
  React.useEffect(() => {
    adminTrpc('auth.me', null, 'query').then((me) => {
      if (!me) {
        window.location.href = '/login?returnTo=/admin';
        return;
      }
      if (me.role !== 'admin') {
        window.location.href = '/app';
        return;
      }
      setUser(me);
      setAuthChecked(true);
    }).catch(() => {
      window.location.href = '/login?returnTo=/admin';
    });
  }, []);

  if (!authChecked) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100vh' }}>
        <div style={{ fontSize: 13, color: 'var(--ink-60)' }}>Checking access…</div>
      </div>
    );
  }

  return (
    <div>
      <div className="adm-top">
        <img src="/adeyy/assets/adeyy-paper.svg" alt="Adeyy" />
        <span className="tag">Admin · ProDesk staff only</span>
        <span className="sp"></span>
        <a href="/app">Vendor app</a>
        <a href="/">Marketing site</a>
        <span className="avatar">{user && user.name ? user.name.slice(0, 2).toUpperCase() : 'PD'}</span>
      </div>

      <div className="adm-tabs">
        {[['vendors', 'Vendors'], ['reserved', 'Reserved endings'], ['health', 'Platform health']].map(([k, l]) => (
          <button key={k} className={tab === k ? 'sel' : ''} onClick={() => setTab(k)}>{l}</button>
        ))}
      </div>

      <main className="amain" style={{ paddingTop: 8 }}>
        {tab === 'vendors' ? <AdminVendors toast={toast} /> : null}
        {tab === 'reserved' ? <AdminReserved toast={toast} /> : null}
        {tab === 'health' ? <AdminHealth /> : null}
      </main>
      <Toasts items={toasts} />
    </div>
  );
}

function AdminVendors({ toast }) {
  const D = window.AdeyyData;
  const [vendors, setVendors] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [q, setQ] = React.useState('');
  const [open, setOpen] = React.useState(null);

  React.useEffect(() => {
    adminTrpc('admin.listVendors', {}, 'query').then((data) => {
      setVendors(data.vendors || []);
      setLoading(false);
    }).catch((err) => {
      console.error('[AdminVendors] load error:', err);
      setLoading(false);
    });
  }, []);

  const shown = vendors.filter((v) => !q || (v.name + v.email).toLowerCase().includes(q.toLowerCase()));
  const totalMrr = vendors.reduce((a, v) => a + (v.mrr || 0), 0);

  if (loading) return <SkeletonTable rows={6} />;

  return (
    <div data-screen-label="A1 Vendors">
      <div className="apage-head">
        <h1>Vendors</h1>
        <span className="meta"><b>{vendors.length}</b> accounts · <b>${totalMrr.toFixed(0)}</b> MRR</span>
        <span className="spacer"></span>
        <div className="search" style={{ position: 'relative', width: 280 }}>
          <input className="ainput" placeholder="Search name or email" value={q} onChange={(e) => setQ(e.target.value)} />
        </div>
      </div>
      {shown.length === 0 ? (
        <EmptyState title="No vendors yet." body="Vendors appear here after they sign up and create their first link." />
      ) : (
        <table className="atable">
          <thead><tr><th>Vendor</th><th>Owner email</th><th className="r">Links</th><th className="r">Active</th><th className="r">MRR</th><th>Billing</th><th>Joined</th></tr></thead>
          <tbody>
            {shown.map((v) => (
              <tr key={v.id} onClick={() => setOpen(v)}>
                <td style={{ fontWeight: 600 }}>{v.name}</td>
                <td className="tdest" style={{ maxWidth: 'none' }}>{v.email}</td>
                <td className="tnum">{v.links}</td>
                <td className="tnum">{v.active}</td>
                <td className="tnum">${(v.mrr || 0).toFixed(2)}</td>
                <td>{v.billingStatus === 'good'
                  ? <span className="status off" style={{ border: 0, color: 'var(--ink-80)' }}><span className="sdot" style={{ background: 'var(--adeyy)', border: 0 }}></span>Good</span>
                  : v.billingStatus === 'card-failed'
                  ? <span className="status off" style={{ borderColor: 'var(--danger)', color: 'var(--danger)' }}><span className="sdot" style={{ borderColor: 'var(--danger)' }}></span>Card failed</span>
                  : <span className="status off" style={{ border: 0, color: 'var(--ink-40)' }}><span className="sdot" style={{ background: 'var(--ink-20)', border: 0 }}></span>No card</span>}
                </td>
                <td className="tdate">{D.fmtDate(v.joined)}</td>
              </tr>
            ))}
          </tbody>
        </table>
      )}

      {open ? (
        <Modal title={open.name} onClose={() => setOpen(null)} width={480}>
          <p style={{ marginBottom: 14 }}>{open.email} · joined {D.fmtDate(open.joined)}</p>
          <div className="kpis three" style={{ marginBottom: 16 }}>
            <Kpi label="Links" value={open.links} />
            <Kpi label="Active" value={open.active} />
            <Kpi label="MRR" value={'$' + (open.mrr || 0).toFixed(0)} />
          </div>
          {open.billingStatus === 'card-failed' ? (
            <p style={{ color: 'var(--danger)', fontSize: 13 }}>Last charge failed. Codes stay live for 14 days, then auto-deactivate.</p>
          ) : null}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            {open.stripeCustomerId ? (
              <a className="abtn abtn-ghost" href={'https://dashboard.stripe.com/customers/' + open.stripeCustomerId} target="_blank" rel="noopener noreferrer">
                Stripe<Icon name="external" size={13} />
              </a>
            ) : null}
            <button className="abtn abtn-primary" onClick={() => { toast('Support session started as ' + open.name); setOpen(null); }}>Support session</button>
          </div>
        </Modal>
      ) : null}
    </div>
  );
}

function AdminReserved({ toast }) {
  const [list, setList] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [slug, setSlug] = React.useState('');
  const [reason, setReason] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  React.useEffect(() => {
    adminTrpc('admin.listReservedSlugs', {}, 'query').then((data) => {
      setList(data.slugs || []);
      setLoading(false);
    }).catch((err) => {
      console.error('[AdminReserved] load error:', err);
      setLoading(false);
    });
  }, []);

  async function reserve() {
    if (!slug || !reason) return;
    setSaving(true);
    try {
      await adminTrpc('admin.reserveSlug', { slug: slug.toLowerCase(), reason }, 'mutation');
      setList((l) => [{ slug: slug.toLowerCase(), reason, createdAt: new Date().toISOString() }].concat(l));
      toast('Reserved ' + slug + '. No vendor can claim it');
      setSlug(''); setReason('');
    } catch (e) {
      toast('Error: ' + e.message);
    } finally {
      setSaving(false);
    }
  }

  async function release(s) {
    try {
      await adminTrpc('admin.releaseSlug', { slug: s }, 'mutation');
      setList((l) => l.filter((x) => x.slug !== s));
      toast(s + ' released');
    } catch (e) {
      toast('Error: ' + e.message);
    }
  }

  return (
    <div data-screen-label="A2 Reserved slugs">
      <div className="apage-head">
        <h1>Reserved endings</h1>
        <span className="meta"><b>{list.length}</b> reserved · plus single characters and the profanity list</span>
      </div>
      <div className="detail-grid" style={{ gridTemplateColumns: '320px 1fr' }}>
        <div className="dcard">
          <div className="dh"><h2>Reserve an ending</h2></div>
          <div className="db" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <input className="ainput mono" placeholder="ending" value={slug} onChange={(e) => setSlug(e.target.value.toLowerCase())} />
            <input className="ainput" placeholder="Reason" value={reason} onChange={(e) => setReason(e.target.value)} />
            <button className="abtn abtn-primary abtn-sm" disabled={!slug || !reason || saving} onClick={reserve}>
              {saving ? 'Reserving…' : 'Reserve'}
            </button>
            <p className="mutetext" style={{ fontSize: 11.5, margin: 0 }}>
              Reserving an ending only blocks new claims. An ending already owned by a vendor is theirs for life.
            </p>
          </div>
        </div>
        <div className="dcard">
          <div className="dh"><h2>Reserved list</h2></div>
          <div className="db">
            {loading ? <SkeletonTable rows={4} /> : (
              <table className="atable">
                <thead><tr><th>Ending</th><th>Reason</th><th></th></tr></thead>
                <tbody>
                  {list.map((r) => (
                    <tr key={r.slug} style={{ cursor: 'default' }}>
                      <td className="tslug">adeyy.com/{r.slug}</td>
                      <td className="mutetext">{r.reason}</td>
                      <td style={{ textAlign: 'right' }}>
                        <button className="abtn abtn-quiet abtn-sm" onClick={() => release(r.slug)}>Release</button>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function AdminHealth() {
  const [health, setHealth] = React.useState(null);
  const [loading, setLoading] = React.useState(true);

  React.useEffect(() => {
    adminTrpc('admin.platformHealth', {}, 'query').then((data) => {
      setHealth(data);
      setLoading(false);
    }).catch((err) => {
      console.error('[AdminHealth] load error:', err);
      setLoading(false);
    });
  }, []);

  /* Fallback latency data for display */
  const lat = health ? health.latency : [21, 24, 22, 19, 23, 26, 22, 20, 24, 31, 23, 21, 22, 25, 20, 19, 23, 22, 27, 24, 21, 20, 22, 23];
  const max = Math.max(...lat, 1);
  const events = health ? health.events : [];

  return (
    <div data-screen-label="A3 Platform health">
      <div className="apage-head">
        <h1>Platform health</h1>
        <span className="meta">Redirects are the product. This page is the heartbeat.</span>
      </div>

      {loading ? <SkeletonTable rows={3} /> : (
        <React.Fragment>
          <div className="kpis">
            <Kpi label="Redirect uptime · 90d" value={health ? health.uptime : '99.998'} unit="%" sub="Target 99.99" accent />
            <Kpi label="P50 redirect latency" value={health ? health.p50 : '23'} unit="ms" sub="At the edge · target under 100" />
            <Kpi label="Redirects · 24h" value={health ? health.redirects24h : '0'} sub="Scans and clicks combined" />
            <Kpi label="Stripe webhooks · 7d" value={health ? health.webhookDelivery : '100'} unit="%" sub="Delivered" />
          </div>

          <div className="chartbox" style={{ marginBottom: 20 }}>
            <div className="ch"><span className="t">Redirect latency · last 24 hours</span><span className="legend"><span><i style={{ background: 'var(--ink)' }}></i>P50 ms</span></span></div>
            <div style={{ display: 'flex', alignItems: 'flex-end', gap: 4, height: 120 }}>
              {lat.map((v, i) => (
                <div key={i} style={{ flex: 1, background: v > 28 ? 'var(--warn)' : 'var(--ink)', borderRadius: '2px 2px 0 0', height: (v / max) * 100 + '%' }} title={v + ' ms'}></div>
              ))}
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--ink-40)', marginTop: 8 }}>
              <span>00:00</span><span>12:00</span><span>23:00</span>
            </div>
          </div>

          <div className="dcard">
            <div className="dh"><h2>Event log</h2><span className="meta">Last 72 hours</span></div>
            <div className="db">
              {events.length === 0 ? (
                <EmptyState title="No events." body="Errors, webhook retries, and rate limit hits appear here." />
              ) : (
                <table className="atable">
                  <thead><tr><th>When</th><th>Kind</th><th>Detail</th></tr></thead>
                  <tbody>
                    {events.map((e, i) => (
                      <tr key={i} style={{ cursor: 'default' }}>
                        <td className="tdate">{e.ts}</td>
                        <td style={{ fontWeight: 600, whiteSpace: 'nowrap' }}>{e.kind}</td>
                        <td className="mutetext">{e.detail}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              )}
            </div>
          </div>
        </React.Fragment>
      )}
    </div>
  );
}

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