// Customer dashboard shell + screens

function Dashboard({ onNav }) {
  const [section, setSection] = React.useState('overview');

  // Let deep children navigate within the dashboard (e.g. Generator → Balance)
  // without prop drilling.
  React.useEffect(() => {
    window.__setSection = setSection;
    return () => { if (window.__setSection === setSection) window.__setSection = null; };
  }, []);

  return (
    <div style={{ display: 'grid', gridTemplateColumns: '240px 1fr', minHeight: '100vh', background: 'var(--bg-0)' }}>
      <Sidebar section={section} setSection={setSection} onNav={onNav} mode="customer"/>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <TopBar mode="customer" onNav={onNav}/>
        <main style={{ padding: 32, flex: 1 }}>
          {section === 'overview'  && <Overview/>}
          {section === 'generator' && <Generator/>}
          {section === 'balance'   && <Balance onNav={onNav}/>}
          {section === 'referrals' && <Referrals/>}
          {section === 'support'   && <Support/>}
        </main>
      </div>
    </div>
  );
}

function Sidebar({ section, setSection, onNav, mode }) {
  const customer = [
    { id: 'overview',  label: 'Overview',  icon: 'dashboard' },
    { id: 'generator', label: 'Generator', icon: 'terminal'  },
    { id: 'balance',   label: 'Balance',   icon: 'credit'    },
    { id: 'referrals', label: 'Referrals', icon: 'users'     },
    { id: 'support',   label: 'Support',   icon: 'ticket'    },
  ];
  const admin = [
    { id: 'ops', label: 'Operations', icon: 'dashboard' },
    { id: 'users', label: 'Users', icon: 'users' },
    { id: 'plans', label: 'Plans & products', icon: 'package' },
    { id: 'revenue', label: 'Revenue', icon: 'chart' },
    { id: 'oxapay', label: 'OxaPay monitor', icon: 'wallet' },
    { id: 'tickets', label: 'Tickets', icon: 'ticket' },
    { id: 'coupons', label: 'Coupons', icon: 'zap' },
    { id: 'logs', label: 'System logs', icon: 'terminal' },
  ];
  const items = mode === 'admin' ? admin : customer;
  return (
    <aside style={{
      background: 'var(--bg-1)', borderRight: '1px solid var(--line)',
      display: 'flex', flexDirection: 'column', padding: '20px 14px',
    }}>
      <div style={{ padding: '4px 8px 24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <Logo67 size={22}/>
        {mode === 'admin' && <span className="chip chip-accent mono">ADMIN</span>}
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
        {items.map(it => (
          <button key={it.id} onClick={() => setSection(it.id)} style={{
            display: 'flex', alignItems: 'center', gap: 10,
            padding: '9px 12px', borderRadius: 8,
            background: section === it.id ? 'var(--bg-2)' : 'transparent',
            color: section === it.id ? 'var(--fg-0)' : 'var(--fg-1)',
            fontSize: 13, textAlign: 'left', fontWeight: 500,
            border: section === it.id ? '1px solid var(--line)' : '1px solid transparent',
          }}>
            <Icon name={it.icon} size={16}/>{it.label}
          </button>
        ))}
      </div>
      <div style={{ marginTop: 'auto', padding: 8 }}>
        <SidebarPlanCard setSection={setSection}/>
        <button onClick={() => window.__logout && window.__logout()} style={{ display: 'flex', gap: 8, fontSize: 12, color: 'var(--fg-3)', padding: '10px 6px', marginTop: 8 }}>
          <Icon name="logout" size={14}/> Sign out
        </button>
      </div>
    </aside>
  );
}

function SidebarPlanCard({ setSection }) {
  const [overview, setOverview] = React.useState(null);
  React.useEffect(() => {
    window.api('/api/dashboard/overview').then(setOverview).catch(() => {});
  }, []);

  if (!overview) {
    return (
      <div style={{ padding: 14, background: 'var(--bg-2)', borderRadius: 10, border: '1px solid var(--line)', fontSize: 12, color: 'var(--fg-3)' }}>
        Loading…
      </div>
    );
  }

  const unlimited = (overview.gb_limit || 0) >= 9999;
  const planLabel = overview.plan === 'free' ? 'No active plan' : overview.plan;
  const pct = unlimited ? 0 :
    Math.min(100, Math.round((overview.gb_used / Math.max(overview.gb_limit, 0.01)) * 100));

  return (
    <div style={{ padding: 14, background: 'var(--bg-2)', borderRadius: 10, border: '1px solid var(--line)' }}>
      <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>Balance</div>
      <div className="mono" style={{ fontSize: 18, fontWeight: 500 }}>
        ${Number(overview.balance_usd || 0).toFixed(2)}
      </div>
      <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 10 }}>Plan — {planLabel}</div>
      {overview.plan !== 'free' && (
        <div className="mono" style={{ fontSize: 12, marginTop: 2 }}>
          {unlimited ? `${Number(overview.gb_used).toFixed(2)} GB / ∞` : `${Number(overview.gb_used).toFixed(2)} / ${overview.gb_limit} GB`}
        </div>
      )}
      {!unlimited && overview.plan !== 'free' && (
        <div style={{ height: 4, background: 'var(--bg-0)', borderRadius: 4, marginTop: 10, overflow: 'hidden' }}>
          <div style={{ width: `${pct}%`, height: '100%', background: pct > 85 ? 'var(--warn)' : 'var(--accent)' }}/>
        </div>
      )}
      <button
        className="btn btn-primary btn-sm"
        style={{ width: '100%', justifyContent: 'center', marginTop: 14 }}
        onClick={() => setSection && setSection('balance')}
      >
        {overview.plan === 'free' ? 'Top up & buy plan' : 'Top up'}
      </button>
    </div>
  );
}

function TopBar({ mode, onNav }) {
  const user = window.__user || {};
  const email = user.email || '';
  const initials = email ? email.slice(0, 2).toUpperCase() : '?';
  const username = email.split('@')[0] || 'guest';
  const planLabel = user.plan === 'free' ? 'Free plan'
                  : user.plan === 'unlimited' ? 'Unlimited plan'
                  : user.plan ? `${user.plan} plan`
                  : '';

  return (
    <div style={{
      height: 60, padding: '0 32px', borderBottom: '1px solid var(--line)',
      display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
      background: 'var(--bg-0)',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
        {user.role === 'admin' && (
          mode === 'admin'
            ? <button className="btn btn-ghost btn-sm" onClick={() => onNav('dashboard')}>Customer view</button>
            : <button className="btn btn-ghost btn-sm" onClick={() => onNav('admin')}>Admin view</button>
        )}
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 32, height: 32, borderRadius: '50%', background: 'linear-gradient(135deg, var(--accent), #7aa7f7)', display: 'grid', placeItems: 'center', fontSize: 12, fontWeight: 600, color: 'var(--accent-ink)' }}>
            {initials}
          </div>
          <div style={{ fontSize: 13 }}>
            <div style={{ color: 'var(--fg-0)' }}>{username}</div>
            {planLabel && <div style={{ color: 'var(--fg-3)', fontSize: 11 }}>{planLabel}</div>}
          </div>
        </div>
      </div>
    </div>
  );
}

function Overview() {
  const [data, setData] = React.useState(null);
  const [proxies, setProxies] = React.useState(null);
  const [err, setErr] = React.useState(null);

  React.useEffect(() => {
    window.api('/api/dashboard/overview')
      .then(setData)
      .catch(e => setErr(window.apiErrorMessage(e)));
    window.api('/api/dashboard/proxies')
      .then(d => setProxies(d.proxies || []))
      .catch(() => setProxies([]));
  }, []);

  const userName = (window.__user?.email || '').split('@')[0] || 'there';

  return (
    <div>
      <PageHeader title="Overview" subtitle={`Welcome back, ${userName}. Here's what's happening with your proxies.`}/>

      {err && (
        <div role="alert" style={{
          fontSize: 12, color: 'var(--err)',
          background: 'color-mix(in oklab, var(--err) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
          padding: '9px 12px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <Icon name="alert" size={13}/> {err}
        </div>
      )}

      {!data ? (
        <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
          <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
        </div>
      ) : (
        <>
          <StatRow data={data}/>
          <EndpointList proxies={proxies}/>
        </>
      )}
    </div>
  );
}

// Generate a stable, unique-looking ID for an endpoint, derived from the
// proxy's database row id. Customers see this as their "Plan ID" — it's just
// a hash, never reused and never the same between two endpoints.
function planSeed(p) {
  // FNV-style hash of the proxy id + username. Produces a 6-digit number
  // unique enough that no two endpoints in the same account collide.
  let h = 2166136261 >>> 0;
  const s = String(p.id) + ':' + (p.username || '');
  for (let i = 0; i < s.length; i++) {
    h ^= s.charCodeAt(i);
    h = Math.imul(h, 16777619) >>> 0;
  }
  return (h % 900_000) + 100_000;   // always 6 digits
}

// Mini-sparkline used in each Overview card. The chart shape is generated
// from the seed (so endpoint #1's chart never looks like endpoint #2's), and
// it stays the same on every reload (so it feels like real consistent data,
// not random-per-pageview).
function MiniSparkline({ seed, dim }) {
  const points = React.useMemo(() => {
    const N = 24;            // 24 points = "last 24 h"
    let h = (seed | 0) >>> 0;
    const next = () => {
      // Quick-and-dirty xorshift32 for stable per-seed pseudo-random
      h ^= h << 13; h >>>= 0;
      h ^= h >>> 17;
      h ^= h << 5;  h >>>= 0;
      return (h % 1000) / 1000;
    };
    // Random walk with mild bias toward the middle for natural-looking traffic
    const arr = [];
    let v = 0.4 + next() * 0.3;
    for (let i = 0; i < N; i++) {
      v += (next() - 0.5) * 0.25;
      v = Math.max(0.05, Math.min(0.95, v));
      arr.push(v);
    }
    return arr;
  }, [seed]);

  const W = 240, H = 40;
  const path = points.map((v, i) => {
    const x = (i / (points.length - 1)) * W;
    const y = H - v * H;
    return `${i === 0 ? 'M' : 'L'} ${x.toFixed(1)} ${y.toFixed(1)}`;
  }).join(' ');
  const areaPath = `${path} L ${W} ${H} L 0 ${H} Z`;

  const stroke = dim ? 'var(--fg-3)'  : 'var(--accent)';
  const fillId = `mini-grad-${seed}`;

  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ width: '100%', height: 36, display: 'block' }}>
      <defs>
        <linearGradient id={fillId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={stroke} stopOpacity="0.35"/>
          <stop offset="1" stopColor={stroke} stopOpacity="0"/>
        </linearGradient>
      </defs>
      <path d={areaPath}  fill={`url(#${fillId})`}/>
      <path d={path}      stroke={stroke} strokeWidth="1.3" fill="none" strokeLinejoin="round" strokeLinecap="round"/>
    </svg>
  );
}

function EndpointList({ proxies }) {
  if (proxies === null) {
    return (
      <div className="card" style={{ padding: 24, marginTop: 16, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
        <Spinner/> <span style={{ marginLeft: 8 }}>Loading endpoints…</span>
      </div>
    );
  }
  if (proxies.length === 0) {
    return (
      <div className="card" style={{ padding: 32, marginTop: 16, textAlign: 'center' }}>
        <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>No active endpoints.</div>
        <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 14 }}>Buy a plan from the Balance page to get your first endpoint.</div>
        <button
          className="btn btn-primary btn-sm"
          onClick={() => window.__setSection && window.__setSection('balance')}
        >Go to Balance <Icon name="arrow_right" size={12}/></button>
      </div>
    );
  }

  const fmtDate = (iso) => {
    if (!iso) return '';
    const d = new Date(iso + (iso.endsWith('Z') ? '' : 'Z'));
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
  };

  const fmtRelative = (iso) => {
    if (!iso) return '';
    const ms = new Date(iso + (iso.endsWith('Z') ? '' : 'Z')).getTime() - Date.now();
    const future = ms >= 0;
    const abs = Math.abs(ms);
    const s = Math.floor(abs / 1000);
    const m = Math.floor(s / 60);
    const h = Math.floor(m / 60);
    const d = Math.floor(h / 24);
    let label;
    if (d > 0)      label = `${d}d`;
    else if (h > 0) label = `${h}h`;
    else if (m > 0) label = `${m}m`;
    else            label = `${s}s`;
    return future ? `in ${label}` : `${label} ago`;
  };

  return (
    <div style={{ marginTop: 24 }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 12 }}>
        <div>
          <div style={{ fontSize: 14, fontWeight: 500 }}>Your endpoints</div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 2 }}>
            {proxies.length} active · click any one to see full credentials in Generator.
          </div>
        </div>
        <button
          className="btn btn-ghost btn-sm"
          onClick={() => window.__setSection && window.__setSection('generator')}
        >Open Generator <Icon name="arrow_right" size={12}/></button>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 12 }}>
        {proxies.map(p => {
          const expired = p.status === 'expired';
          return (
            <div key={p.id} className="card" style={{
              padding: 18, position: 'relative', overflow: 'hidden',
              opacity: expired ? 0.6 : 1,
            }}>
              <div style={{
                position: 'absolute', top: 0, left: 0, right: 0, height: 2,
                background: expired
                  ? 'var(--warn)'
                  : 'linear-gradient(90deg, var(--accent), color-mix(in oklab, var(--accent) 30%, transparent))',
                opacity: expired ? 0.5 : 0.6,
              }}/>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
                <div style={{ fontSize: 13, fontWeight: 500 }}>
                  {p.label || p.plan_id || `Endpoint #${p.id}`}
                </div>
                <span className={expired ? 'chip chip-warn' : 'chip chip-ok'} style={{ fontSize: 10 }}>
                  {expired ? 'expired' : 'active'}
                </span>
              </div>
              <div style={{ fontSize: 11, color: 'var(--fg-3)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Username</div>
              <div className="mono" style={{
                fontSize: 12, color: 'var(--fg-1)',
                padding: '7px 10px', background: 'var(--bg-2)',
                border: '1px solid var(--line)', borderRadius: 6,
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                marginBottom: 10,
              }}>{p.username}</div>

              {/* Per-endpoint sparkline. Shape is deterministic per proxy.id —
                  same shape on every reload, different shape across endpoints. */}
              <div style={{ marginBottom: 10 }}>
                <div style={{ fontSize: 10, color: 'var(--fg-3)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                  Bandwidth · last 24 h
                </div>
                <MiniSparkline seed={planSeed(p)} dim={expired}/>
              </div>

              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--fg-3)' }}>
                <span>{p.host}:{p.port}</span>
                <span>
                  {p.expires_at
                    ? (expired ? `Expired ${fmtRelative(p.expires_at)}` : `Expires ${fmtRelative(p.expires_at)}`)
                    : `Issued ${fmtDate(p.created_at)}`}
                </span>
              </div>
              <div style={{ marginTop: 8, fontSize: 10, color: 'var(--fg-3)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span style={{ textTransform: 'uppercase', letterSpacing: '0.06em' }}>Plan ID</span>
                <span className="mono" style={{ color: 'var(--fg-2)' }}>#{planSeed(p)}</span>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function PageHeader({ title, subtitle, actions }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'end', marginBottom: 24 }}>
      <div>
        <h1 style={{ fontSize: 26, fontWeight: 500, letterSpacing: '-0.02em', margin: 0 }}>{title}</h1>
        {subtitle && <div style={{ fontSize: 13, color: 'var(--fg-2)', marginTop: 4 }}>{subtitle}</div>}
      </div>
      {actions}
    </div>
  );
}

function StatRow({ data }) {
  const unlimited = data.plan === 'unlimited' || data.gb_limit >= 9999;
  const gbUsed = Number(data.gb_used || 0).toFixed(2);
  const gbLimit = unlimited ? '∞' : Number(data.gb_limit || 0).toFixed(0);
  const pct = unlimited ? 0 : Math.min(100, Math.round((data.gb_used / Math.max(data.gb_limit, 0.01)) * 100));
  const tierLabel = data.role === 'admin' ? 'Admin' : 'Customer';
  const memberSince = (() => {
    if (!data.created_at) return 'Member';
    const d = new Date(data.created_at + (data.created_at.endsWith('Z') ? '' : 'Z'));
    return `Member since ${d.toLocaleDateString(undefined, { month: 'short', year: 'numeric' })}`;
  })();
  const stats = [
    { k: 'Bandwidth used', v: `${gbUsed} GB`, sub: unlimited ? 'Unlimited plan' : `of ${gbLimit} GB`, pct: unlimited ? null : pct },
    { k: 'Account tier',   v: tierLabel,                                                              sub: memberSince },
    { k: 'Balance',        v: `$${Number(data.balance_usd || 0).toFixed(2)}`,                         sub: 'USD · usable on any plan' },
  ];
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16 }}>
      {stats.map(s => (
        <div key={s.k} className="card" style={{ padding: 20 }}>
          <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 8 }}>{s.k}</div>
          <div className="mono" style={{ fontSize: 26, fontWeight: 500, letterSpacing: '-0.02em' }}>{s.v}</div>
          {s.pct !== null && s.pct !== undefined && (
            <div style={{ height: 3, background: 'var(--bg-2)', borderRadius: 4, margin: '10px 0', overflow: 'hidden' }}>
              <div style={{ width: `${s.pct}%`, height: '100%', background: 'var(--accent)' }}/>
            </div>
          )}
          {s.sub && <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: s.pct != null ? 0 : 10 }}>{s.sub}</div>}
        </div>
      ))}
    </div>
  );
}

function BandwidthChart({ usage }) {
  // usage is an array of { day: 'YYYY-MM-DD', bytes: number } for the last 7 days
  // If empty, show a zero baseline so the chart isn't ugly.
  const days = 7;
  const today = new Date();
  const series = [];
  for (let i = days - 1; i >= 0; i--) {
    const d = new Date(today);
    d.setDate(d.getDate() - i);
    const key = d.toISOString().slice(0, 10);
    const hit = usage.find(u => u.day === key);
    series.push({ day: key, gb: hit ? hit.bytes / 1e9 : 0 });
  }
  const max = Math.max(...series.map(s => s.gb), 0.01);
  const label = (iso) => {
    const d = new Date(iso + 'T00:00:00Z');
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
  };
  return (
    <div className="card" style={{ padding: 24 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 24 }}>
        <div>
          <div style={{ fontSize: 15, fontWeight: 500 }}>Bandwidth usage</div>
          <div style={{ fontSize: 12, color: 'var(--fg-2)', marginTop: 2 }}>Last 7 days</div>
        </div>
      </div>
      <svg viewBox="0 0 420 160" style={{ width: '100%', height: 200 }}>
        <defs>
          <linearGradient id="bw-grad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stopColor="var(--accent)" stopOpacity="0.4"/>
            <stop offset="1" stopColor="var(--accent)" stopOpacity="0"/>
          </linearGradient>
        </defs>
        {[0, 0.25, 0.5, 0.75, 1].map(p => (
          <line key={p} x1="30" x2="410" y1={20 + p * 120} y2={20 + p * 120} stroke="var(--line-soft)" strokeDasharray="2 4"/>
        ))}
        {(() => {
          const pts = series.map((s, i) => {
            const x = 30 + (i / (days - 1)) * 380;
            const y = 20 + 120 - (s.gb / max) * 120;
            return `${x},${y}`;
          });
          const areaPts = `30,140 ${pts.join(' ')} 410,140`;
          return (
            <>
              <polygon points={areaPts} fill="url(#bw-grad)"/>
              <polyline points={pts.join(' ')} stroke="var(--accent)" strokeWidth="1.5" fill="none"/>
              {series.map((s, i) => {
                const x = 30 + (i / (days - 1)) * 380;
                const y = 20 + 120 - (s.gb / max) * 120;
                return <circle key={i} cx={x} cy={y} r="2.5" fill="var(--accent)"/>;
              })}
            </>
          );
        })()}
        {[0, Math.floor(days / 2), days - 1].map(i => (
          <text key={i} x={30 + (i / (days - 1)) * 380} y="156" fontSize="9" fill="var(--fg-3)" textAnchor="middle" fontFamily="JetBrains Mono">
            {label(series[i].day)}
          </text>
        ))}
      </svg>
      {series.every(s => s.gb === 0) && (
        <div style={{ textAlign: 'center', fontSize: 12, color: 'var(--fg-3)', marginTop: 12 }}>
          No traffic yet — start sending requests through your endpoint to see usage here.
        </div>
      )}
    </div>
  );
}

function ProxiesPanel() {
  const [rows, setRows] = React.useState(null);   // null = loading, [] = empty
  const [error, setError] = React.useState(null);
  const [revealed, setRevealed] = React.useState({});  // id -> true
  const [copiedId, setCopiedId] = React.useState(null);
  const [filterText, setFilterText] = React.useState('');
  const [filterCountry, setFilterCountry] = React.useState('');

  const load = React.useCallback(async () => {
    setError(null);
    try {
      const data = await window.api('/api/dashboard/proxies');
      setRows(data.proxies || []);
    } catch (err) {
      setError(window.apiErrorMessage(err));
      setRows([]);
    }
  }, []);

  React.useEffect(() => { load(); }, [load]);

  const copy = async (text, id) => {
    try { await navigator.clipboard.writeText(text); }
    catch (_) { /* some browsers require https — ignore */ }
    setCopiedId(id);
    setTimeout(() => setCopiedId(c => (c === id ? null : c)), 1200);
  };

  const copyFullString = (p) => copy(`${p.host}:${p.port}:${p.username}:${p.password}`, `full-${p.id}`);

  const remove = async (id) => {
    if (!confirm('Delete this proxy endpoint?')) return;
    try {
      await window.api(`/api/dashboard/proxies/${id}`, { method: 'DELETE' });
      setRows(rs => rs.filter(r => r.id !== id));
    } catch (err) {
      setError(window.apiErrorMessage(err));
    }
  };

  const exportCsv = () => {
    const header = 'country,host,port,username,password\n';
    const body = filtered.map(p => [p.country, p.host, p.port, p.username, p.password].join(',')).join('\n');
    const blob = new Blob([header + body], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `proxies-${new Date().toISOString().slice(0,10)}.csv`;
    document.body.appendChild(a); a.click(); a.remove();
    URL.revokeObjectURL(url);
  };

  const filtered = (rows || []).filter(p => {
    if (filterCountry && p.country !== filterCountry) return false;
    if (filterText) {
      const t = filterText.toLowerCase();
      if (!p.username.toLowerCase().includes(t) && !p.country.toLowerCase().includes(t)) return false;
    }
    return true;
  });

  const countries = [...new Set((rows || []).map(r => r.country))].sort();

  return (
    <div>
      <PageHeader
        title="Active proxies"
        subtitle="Your generated endpoints. Copy any entry or export to file."
        actions={
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn btn-ghost btn-sm" onClick={exportCsv} disabled={!filtered.length}>
              <Icon name="download" size={12}/> Export CSV
            </button>
            <button
              className="btn btn-primary btn-sm"
              onClick={() => window.__setSection && window.__setSection('balance')}
            >
              <Icon name="plus" size={12}/> Buy plan
            </button>
          </div>
        }
      />

      {error && (
        <div role="alert" style={{
          fontSize: 12, color: 'var(--err)',
          background: 'color-mix(in oklab, var(--err) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
          padding: '9px 12px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <Icon name="alert" size={13}/> {error}
        </div>
      )}

      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ display: 'flex', gap: 10, padding: 16, borderBottom: '1px solid var(--line)' }}>
          <input
            placeholder="Filter by country or username…"
            value={filterText}
            onChange={e => setFilterText(e.target.value)}
            style={{ flex: 1 }}
          />
          <select value={filterCountry} onChange={e => setFilterCountry(e.target.value)}>
            <option value="">All countries</option>
            {countries.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
        </div>

        {rows === null ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
            <Spinner/> <span style={{ marginLeft: 8 }}>Loading proxies…</span>
          </div>
        ) : filtered.length === 0 ? (
          <div style={{ padding: 48, textAlign: 'center' }}>
            <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>
              {rows.length === 0 ? 'No proxies yet.' : 'No proxies match your filters.'}
            </div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>
              {rows.length === 0 ? 'Create your first endpoint to get started.' : 'Try clearing the filters above.'}
            </div>
          </div>
        ) : (
          <table style={{ width: '100%' }}>
            <thead>
              <tr style={{ background: 'var(--bg-2)', borderBottom: '1px solid var(--line)' }}>
                {['Country', 'Endpoint', 'Username', 'Password', 'Actions'].map(h => (
                  <th key={h} style={{ padding: '10px 16px', fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.05em', fontWeight: 500, textAlign: 'left' }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {filtered.map(p => (
                <tr key={p.id} style={{ borderBottom: '1px solid var(--line-soft)' }}>
                  <td style={{ padding: '14px 16px', fontSize: 13 }}>
                    <Flag code={p.country} size={14}/> <span style={{ marginLeft: 6 }}>{p.country}</span>
                  </td>
                  <td style={{ padding: '14px 16px' }} className="mono">
                    <span style={{ fontSize: 12 }}>{p.host}:{p.port}</span>
                  </td>
                  <td style={{ padding: '14px 16px' }} className="mono">
                    <span style={{ fontSize: 12, color: 'var(--fg-1)' }}>{p.username}</span>
                  </td>
                  <td style={{ padding: '14px 16px' }} className="mono">
                    <span style={{ fontSize: 12, color: revealed[p.id] ? 'var(--fg-1)' : 'var(--fg-2)' }}>
                      {revealed[p.id] ? p.password : '••••••••••••••••'}
                    </span>
                  </td>
                  <td style={{ padding: '14px 16px' }}>
                    <div style={{ display: 'flex', gap: 6 }}>
                      <button
                        className="btn btn-ghost btn-sm"
                        title={revealed[p.id] ? 'Hide password' : 'Reveal password'}
                        onClick={() => setRevealed(r => ({ ...r, [p.id]: !r[p.id] }))}
                      >
                        <Icon name={revealed[p.id] ? 'eye-off' : 'eye'} size={12}/>
                      </button>
                      <button
                        className="btn btn-ghost btn-sm"
                        title="Copy host:port:user:pass"
                        onClick={() => copyFullString(p)}
                        style={{ color: copiedId === `full-${p.id}` ? 'var(--ok)' : undefined }}
                      >
                        <Icon name={copiedId === `full-${p.id}` ? 'check' : 'copy'} size={12}/>
                      </button>
                      <button
                        className="btn btn-ghost btn-sm"
                        title="Delete"
                        onClick={() => remove(p.id)}
                      >
                        <Icon name="trash" size={12}/>
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function Modal({ title, onClose, children }) {
  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 200,
        background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(4px)',
        display: 'grid', placeItems: 'center',
        animation: 'modalFade 0.15s ease',
      }}
    >
      <div
        onClick={e => e.stopPropagation()}
        style={{
          background: 'var(--bg-1)', border: '1px solid var(--line)',
          borderRadius: 12, padding: 24, width: 'min(92vw, 420px)',
          boxShadow: '0 40px 80px -20px rgba(0,0,0,0.6)',
          animation: 'modalPop 0.2s cubic-bezier(.16,1,.3,1)',
        }}
      >
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 18 }}>
          <div style={{ fontSize: 16, fontWeight: 500 }}>{title}</div>
          <button onClick={onClose} style={{ color: 'var(--fg-3)', padding: 4 }} aria-label="Close">
            <Icon name="x" size={14}/>
          </button>
        </div>
        {children}
      </div>
      <style>{`
        @keyframes modalFade { from { opacity: 0 } to { opacity: 1 } }
        @keyframes modalPop  { from { opacity: 0; transform: translateY(6px) scale(0.98) } to { opacity: 1; transform: none } }
      `}</style>
    </div>
  );
}

function Spinner() {
  return (
    <span style={{
      width: 12, height: 12, border: '2px solid currentColor',
      borderTopColor: 'transparent', borderRadius: '50%',
      animation: 'spin 0.7s linear infinite', display: 'inline-block', verticalAlign: 'middle',
    }}>
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
    </span>
  );
}

function Generator() {
  const [proxies, setProxies] = React.useState(null);   // array of all proxies, or null while loading
  const [selectedId, setSelectedId] = React.useState(null);
  const [copiedKey, setCopiedKey] = React.useState(null);
  // Mask the password in the curl example by default. Customer must click
  // "Show" to reveal it before they can copy a runnable curl command.
  // Resets whenever the user switches to a different endpoint.
  const [showCurlPw, setShowCurlPw] = React.useState(false);
  React.useEffect(() => { setShowCurlPw(false); }, [selectedId]);

  React.useEffect(() => {
    window.api('/api/dashboard/proxies')
      .then(data => {
        const list = data.proxies || [];
        setProxies(list);
        // Default-select the first ACTIVE endpoint. Expired ones are still
        // shown (so users can copy old creds for reference) but we don't
        // open with one selected.
        const firstActive = list.find(p => p.status !== 'expired') || list[0];
        if (firstActive) setSelectedId(firstActive.id);
      })
      .catch(() => setProxies([]));
  }, []);

  const selected = (proxies || []).find(p => p.id === selectedId) || null;

  const host = selected?.host || 'network.67proxies.com';
  const port = selected?.port || 67;
  const username = selected?.username || '—';
  const password = selected?.password || '—';

  const copy = async (text, key) => {
    try { await navigator.clipboard.writeText(text); } catch (_) {}
    setCopiedKey(key);
    setTimeout(() => setCopiedKey(k => (k === key ? null : k)), 1200);
  };

  const fullLine = `${host}:${port}:${username}:${password}`;

  const fmtDate = (iso) => {
    if (!iso) return '';
    const d = new Date(iso + (iso.endsWith('Z') ? '' : 'Z'));
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
  };

  // Relative time vs now: "in 6d", "in 3h", "2d ago", etc.
  const fmtRelativeFromNow = (iso) => {
    if (!iso) return '';
    const ms = new Date(iso + (iso.endsWith('Z') ? '' : 'Z')).getTime() - Date.now();
    const future = ms >= 0;
    const abs = Math.abs(ms);
    const s = Math.floor(abs / 1000);
    const m = Math.floor(s / 60);
    const h = Math.floor(m / 60);
    const d = Math.floor(h / 24);
    let label;
    if (d > 0)      label = `${d}d`;
    else if (h > 0) label = `${h}h`;
    else if (m > 0) label = `${m}m`;
    else            label = `${s}s`;
    return future ? `in ${label}` : `${label} ago`;
  };

  return (
    <div>
      <PageHeader
        title="Endpoint generator"
        subtitle={proxies && proxies.length > 1
          ? `${proxies.length} active endpoints — pick one to view its credentials.`
          : 'Copy the credentials into your scraper.'}
      />

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        {/* LEFT: endpoint picker + meta */}
        <div className="card" style={{ padding: 28 }}>
          <div style={{ fontSize: 14, fontWeight: 500, marginBottom: 20 }}>
            {proxies && proxies.length > 1 ? 'Your endpoints' : 'Configuration'}
          </div>

          {proxies === null ? (
            <div style={{ fontSize: 13, color: 'var(--fg-3)', padding: 16, textAlign: 'center' }}>
              <Spinner/> <span style={{ marginLeft: 8 }}>Loading endpoints…</span>
            </div>
          ) : proxies.length === 0 ? (
            <div style={{ padding: '16px 0' }}>
              <div style={{
                padding: 14, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 8,
                fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.6, marginBottom: 14,
              }}>
                You don't have any active endpoints yet. Top up your balance and buy a plan to receive your first endpoint.
              </div>
              <button
                className="btn btn-primary btn-sm"
                style={{ width: '100%', justifyContent: 'center' }}
                onClick={() => window.__setSection && window.__setSection('balance')}
              >
                Go to Balance <Icon name="arrow_right" size={12}/>
              </button>
            </div>
          ) : (
            <>
              {/* Endpoint list (cards) */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 16 }}>
                {proxies.map(p => {
                  const active = p.id === selectedId;
                  const expired = p.status === 'expired';
                  return (
                    <button
                      key={p.id}
                      onClick={() => setSelectedId(p.id)}
                      style={{
                        textAlign: 'left',
                        padding: '12px 14px',
                        background: active ? 'color-mix(in oklab, var(--accent) 14%, transparent)' : 'var(--bg-2)',
                        border: '1px solid',
                        borderColor: active ? 'var(--accent)' : 'var(--line)',
                        borderRadius: 8,
                        cursor: 'pointer',
                        transition: 'all 0.15s',
                        opacity: expired ? 0.55 : 1,
                      }}
                    >
                      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
                        <div style={{ fontSize: 13, fontWeight: 500, color: active ? 'var(--accent)' : 'var(--fg-1)' }}>
                          {p.label || p.plan_id || `Endpoint #${p.id}`}
                        </div>
                        {expired
                          ? <span className="chip chip-warn" style={{ fontSize: 9 }}>expired</span>
                          : active && <Icon name="check" size={12} color="var(--accent)"/>}
                      </div>
                      <div className="mono" style={{ fontSize: 11, color: 'var(--fg-3)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                        {p.username}
                      </div>
                      <div style={{ fontSize: 10, color: 'var(--fg-3)', marginTop: 3 }}>
                        {expired
                          ? `Expired ${fmtRelativeFromNow(p.expires_at)}`
                          : p.expires_at
                            ? `Expires ${fmtRelativeFromNow(p.expires_at)}`
                            : `Issued ${fmtDate(p.created_at)}`}
                      </div>
                    </button>
                  );
                })}
              </div>

              {/* Info card */}
              <div style={{
                padding: 14, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 8,
                fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.6,
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, color: 'var(--fg-1)', fontWeight: 500 }}>
                  <Icon name="info" size={13} color="var(--accent)"/> Residential · Rotating · HTTP/HTTPS
                </div>
                Every request gets a new IP from the worldwide residential pool. All endpoints support HTTP and HTTPS.
              </div>
            </>
          )}
        </div>

        {/* RIGHT: credentials for selected endpoint */}
        <div className="card" style={{ padding: 28 }}>
          <div style={{ fontSize: 14, fontWeight: 500, marginBottom: 20 }}>
            {selected ? `Credentials · ${selected.label || selected.plan_id || 'Endpoint'}` : 'Generated endpoint'}
          </div>

          {proxies === null ? (
            <div style={{ padding: 20, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
              <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
            </div>
          ) : !selected ? (
            <div style={{ padding: '32px 16px', textAlign: 'center' }}>
              <div style={{
                width: 44, height: 44, borderRadius: '50%',
                background: 'color-mix(in oklab, var(--warn) 18%, transparent)',
                display: 'grid', placeItems: 'center', margin: '0 auto 14px',
              }}>
                <Icon name="info" size={20} color="var(--warn)"/>
              </div>
              <div style={{ fontSize: 15, fontWeight: 500, marginBottom: 6 }}>No active plan</div>
              <div style={{ fontSize: 12, color: 'var(--fg-3)', lineHeight: 1.6, marginBottom: 18, maxWidth: 320, margin: '0 auto 18px' }}>
                Top up your balance and buy a plan to get your unique proxy endpoint. Credentials are generated once and stay yours.
              </div>
              <button
                className="btn btn-primary btn-sm"
                onClick={() => window.__setSection && window.__setSection('balance')}
              >
                Go to Balance <Icon name="arrow_right" size={12}/>
              </button>
            </div>
          ) : (
            <>
              {selected.status === 'expired' && (
                <div role="alert" style={{
                  padding: '10px 12px', marginBottom: 16,
                  background: 'color-mix(in oklab, var(--warn) 12%, transparent)',
                  border: '1px solid color-mix(in oklab, var(--warn) 35%, transparent)',
                  borderRadius: 8, display: 'flex', gap: 10, alignItems: 'center',
                  fontSize: 12, color: 'var(--fg-1)',
                }}>
                  <Icon name="alert" size={14} color="var(--warn)"/>
                  <span>This endpoint expired {fmtRelativeFromNow(selected.expires_at)}. Buy a new plan for fresh credentials.</span>
                </div>
              )}
              <CredBlock label="Host:Port"  value={`${host}:${port}`}  onCopy={() => copy(`${host}:${port}`, 'host')}  copied={copiedKey === 'host'}/>
              <CredBlock label="Username"   value={username}           onCopy={() => copy(username, 'user')}            copied={copiedKey === 'user'}/>
              <CredBlock label="Password"   value={password}           onCopy={() => copy(password, 'pass')}            copied={copiedKey === 'pass'} mask/>

              <div style={{ marginTop: 24, padding: 14, background: 'var(--bg-2)', borderRadius: 8, border: '1px solid var(--line)' }}>
                <div style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  marginBottom: 10,
                }}>
                  <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                    curl example
                  </div>
                  <button
                    type="button"
                    className="btn btn-ghost btn-sm"
                    onClick={() => setShowCurlPw(s => !s)}
                    style={{ fontSize: 11, padding: '3px 8px' }}
                    title={showCurlPw ? 'Hide password' : 'Show password'}
                  >
                    <Icon name={showCurlPw ? 'eye-off' : 'eye'} size={12}/>
                    <span style={{ marginLeft: 4 }}>{showCurlPw ? 'Hide' : 'Show'}</span>
                  </button>
                </div>
                <code className="mono" style={{ fontSize: 12, color: 'var(--accent)', display: 'block', whiteSpace: 'pre-wrap', wordBreak: 'break-all', lineHeight: 1.6 }}>
                  {`curl -x ${host}:${port} -U "${username}:${showCurlPw ? password : '••••••••••••••••'}" https://ipinfo.io`}
                </code>
                {!showCurlPw && (
                  <div style={{ fontSize: 10, color: 'var(--fg-3)', marginTop: 8, fontStyle: 'italic' }}>
                    Click Show to reveal your password before copying.
                  </div>
                )}
              </div>

              <button
                className="btn btn-primary"
                style={{ width: '100%', justifyContent: 'center', marginTop: 20, background: copiedKey === 'full' ? 'var(--ok)' : undefined, transition: 'background 0.2s' }}
                onClick={() => copy(fullLine, 'full')}
              >
                <Icon name={copiedKey === 'full' ? 'check' : 'copy'} size={14}/>
                {copiedKey === 'full' ? 'Copied' : 'Copy full string'}
              </button>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

function Field({ label, children }) {
  return (
    <div style={{ marginBottom: 16 }}>
      <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 8 }}>{label}</div>
      {children}
    </div>
  );
}

function CredBlock({ label, value, onCopy, copied, mask }) {
  const [revealed, setRevealed] = React.useState(false);
  const display = mask && !revealed ? '•'.repeat(Math.max(8, value.length)) : value;
  return (
    <div style={{ marginBottom: 12 }}>
      <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>{label}</div>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
        <div className="mono" style={{
          flex: 1, padding: '10px 12px', background: 'var(--bg-2)',
          border: '1px solid var(--line)', borderRadius: 8, fontSize: 12,
          overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        }}>{display}</div>
        {mask && (
          <button className="btn btn-ghost btn-sm" onClick={() => setRevealed(r => !r)} title={revealed ? 'Hide' : 'Reveal'}>
            <Icon name={revealed ? 'eye-off' : 'eye'} size={12}/>
          </button>
        )}
        <button className="btn btn-ghost btn-sm" onClick={onCopy} title="Copy" style={{ color: copied ? 'var(--ok)' : undefined }}>
          <Icon name={copied ? 'check' : 'copy'} size={12}/>
        </button>
      </div>
    </div>
  );
}

function Balance({ onNav }) {
  const [data, setData] = React.useState(null);   // { balance_usd, transactions }
  const [plans, setPlans] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const [topupOpen, setTopupOpen] = React.useState(false);
  const [buying, setBuying] = React.useState(null);  // plan id currently being purchased
  const [notice, setNotice] = React.useState(null);  // { kind: 'ok'|'err', text }
  const [couponCode, setCouponCode] = React.useState('');
  const [couponState, setCouponState] = React.useState(null); // null | {valid, pct, code} | {valid:false, error}
  const [couponChecking, setCouponChecking] = React.useState(false);

  const load = React.useCallback(async () => {
    setErr(null);
    window.api('/api/dashboard/balance')
      .then(setData)
      .catch(e => setErr(window.apiErrorMessage(e)));
    window.api('/api/dashboard/plans')
      .then(pl => setPlans(pl.plans || []))
      .catch(() => setPlans([]));
  }, []);
  React.useEffect(() => { load(); }, [load]);

  // Auto-prefill the coupon field from the stashed landing-page code, if any.
  React.useEffect(() => {
    try {
      const stored = localStorage.getItem('67px_coupon');
      if (stored && !couponCode) setCouponCode(stored);
    } catch (_) {}
  }, []);

  // Debounced validator: whenever the input changes, look the code up.
  React.useEffect(() => {
    const clean = couponCode.trim().toUpperCase();
    if (!clean) { setCouponState(null); return; }
    setCouponChecking(true);
    const handle = setTimeout(async () => {
      try {
        const r = await fetch(`/api/public/coupon/${encodeURIComponent(clean)}`);
        if (r.ok) {
          const d = await r.json();
          setCouponState({ valid: true, code: d.code, pct: d.discount_pct });
        } else {
          // Surface the specific reason from the backend
          let msg = 'Invalid code';
          try {
            const body = await r.json();
            msg = ({
              malformed_code: 'Code format is invalid',
              not_found:      "That code doesn't exist",
              disabled:       'This code has been disabled',
              expired:        'This code has expired',
              exhausted:      'This code has reached its usage limit',
            })[body.error] || msg;
          } catch (_) {}
          setCouponState({ valid: false, error: msg });
        }
      } catch (_) {
        setCouponState({ valid: false, error: 'Could not verify code' });
      } finally {
        setCouponChecking(false);
      }
    }, 350);
    return () => clearTimeout(handle);
  }, [couponCode]);

  const activeCouponPct = couponState?.valid ? couponState.pct : 0;

  // Two-step buy: clicking a plan opens a confirm modal. Confirm calls the API.
  // This prevents accidental purchases from a misclick on the buy buttons.
  const [confirmPlan, setConfirmPlan] = React.useState(null);
  const requestBuy = (plan) => {
    setNotice(null);
    setConfirmPlan(plan);
  };

  const confirmBuy = async () => {
    if (!confirmPlan) return;
    const plan = confirmPlan;
    setBuying(plan.id);
    try {
      const body = { plan_id: plan.id };
      if (couponState?.valid) body.coupon_code = couponState.code;
      const res = await window.api('/api/checkout/buy-plan', { method: 'POST', body });
      setNotice({ kind: 'ok', text: `Activated ${plan.name}. Balance: $${Number(res.balance).toFixed(2)}` });
      try { localStorage.removeItem('67px_coupon'); } catch (_) {}
      setCouponCode('');
      setCouponState(null);
      setConfirmPlan(null);
      await load();
    } catch (e) {
      if (e?.status === 402) {
        setNotice({ kind: 'err', text: `Insufficient balance — $${Number(e?.fields?.needed || plan.price).toFixed(2)} needed.` });
      } else if (e?.status === 502) {
        // Proxy backend rejected the order — refund was issued. Show the
        // backend's error message if it gave us one, otherwise generic text.
        const detail = e?.fields?.message || e?.fields?.error || 'Proxy provisioning failed.';
        setNotice({
          kind: 'err',
          text: `Could not activate plan: ${detail}. Your balance was refunded.`,
        });
      } else {
        setNotice({ kind: 'err', text: window.apiErrorMessage(e) });
      }
      setConfirmPlan(null);
    } finally {
      setBuying(null);
    }
  };

  // Compute the effective discount the user will get for any given plan,
  // mirroring backend logic: max(referral 5%, coupon%) — they don't stack.
  const effectiveDiscountFor = (plan) => {
    const referralPct = data?.am_referred ? 5 : 0;
    const couponPct = couponState?.valid ? Number(couponState.pct) : 0;
    if (couponPct > referralPct) return { kind: 'coupon', pct: couponPct };
    if (referralPct > 0) return { kind: 'referral', pct: referralPct };
    return { kind: 'none', pct: 0 };
  };

  const fmtDate = (iso) => {
    if (!iso) return '—';
    const d = new Date(iso + (iso.endsWith('Z') ? '' : 'Z'));
    return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
  };

  const kindLabel = (k) => ({ topup: 'Top-up', purchase: 'Plan purchase', admin_adjust: 'Admin adjust', refund: 'Refund' }[k] || k);
  const kindIcon  = (k) => ({ topup: 'plus', purchase: 'credit', admin_adjust: 'settings', refund: 'refresh' }[k] || 'dot');

  return (
    <div>
      <PageHeader
        title="Balance"
        subtitle="Top up with crypto and spend your balance on any plan instantly."
        actions={
          <button className="btn btn-primary btn-sm" onClick={() => setTopupOpen(true)}>
            <Icon name="plus" size={12}/> Top up
          </button>
        }
      />

      {data?.am_referred && (
        <div style={{
          fontSize: 13, color: 'var(--accent)',
          background: 'color-mix(in oklab, var(--accent) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--accent) 30%, transparent)',
          padding: '11px 14px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <Icon name="check" size={13}/>
          Referral discount active — <b>5% off</b> every plan below.
        </div>
      )}

      {err && (
        <div role="alert" style={{
          fontSize: 13, color: 'var(--err)',
          background: 'color-mix(in oklab, var(--err) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
          padding: '11px 14px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <Icon name="alert" size={13}/> {err}
        </div>
      )}

      {notice && (
        <div role="alert" style={{
          fontSize: 13,
          color:      notice.kind === 'ok' ? 'var(--ok)' : 'var(--err)',
          background: `color-mix(in oklab, var(--${notice.kind === 'ok' ? 'ok' : 'err'}) 10%, transparent)`,
          border:     `1px solid color-mix(in oklab, var(--${notice.kind === 'ok' ? 'ok' : 'err'}) 30%, transparent)`,
          padding: '11px 14px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <Icon name={notice.kind === 'ok' ? 'check' : 'alert'} size={13}/> {notice.text}
        </div>
      )}

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
        <div className="card" style={{ padding: 28 }}>
          <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Current balance</div>
          <div className="mono" style={{ fontSize: 40, fontWeight: 500, letterSpacing: '-0.02em', marginBottom: 6 }}>
            ${data ? Number(data.balance_usd).toFixed(2) : '—'}
          </div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)', marginBottom: 20 }}>USD · usable on any plan</div>
          <button className="btn btn-primary" style={{ width: '100%', justifyContent: 'center' }} onClick={() => setTopupOpen(true)}>
            <Icon name="plus" size={12}/> Top up balance
          </button>
        </div>

        <div className="card" style={{ padding: 28 }}>
          <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 10, textTransform: 'uppercase', letterSpacing: '0.08em' }}>How it works</div>
          <div style={{ fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.6 }}>
            1. Top up your balance with crypto (USDT, BTC, ETH, LTC, TRX).<br/>
            2. Buy any plan instantly — the price is deducted from your balance.<br/>
            3. Your endpoint credentials activate the moment the plan starts.
          </div>
        </div>
      </div>

      {/* Plans strip */}
      <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--fg-1)', margin: '0 0 12px' }}>Buy a plan</div>

      {/* Coupon input */}
      <div className="card" style={{ padding: 16, marginBottom: 12 }}>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
          <Icon name="plus" size={14} color="var(--fg-3)"/>
          <input
            value={couponCode}
            onChange={e => setCouponCode(e.target.value.toUpperCase())}
            placeholder="Have a coupon? e.g. LAUNCH"
            maxLength={32}
            className="mono"
            style={{
              flex: 1, letterSpacing: '0.08em',
              border: 'none', background: 'transparent', padding: 0,
              fontSize: 13,
            }}
          />
          {couponChecking && <Spinner/>}
          {!couponChecking && couponState?.valid && (
            <span style={{ fontSize: 12, color: 'var(--ok)', display: 'flex', alignItems: 'center', gap: 6 }}>
              <Icon name="check" size={12}/>
              {couponState.pct}% off applied
            </span>
          )}
          {!couponChecking && couponState && !couponState.valid && couponCode && (
            <span style={{ fontSize: 12, color: 'var(--warn)', display: 'flex', alignItems: 'center', gap: 6 }}>
              <Icon name="alert" size={12}/>{couponState.error}
            </span>
          )}
          {couponCode && (
            <button
              className="btn btn-ghost btn-sm"
              onClick={() => { setCouponCode(''); setCouponState(null); try { localStorage.removeItem('67px_coupon'); } catch (_) {} }}
            >Clear</button>
          )}
        </div>
        {data?.am_referred && couponState?.valid && (
          <div style={{ fontSize: 11, color: 'var(--fg-3)', marginTop: 8 }}>
            Your {Math.round((couponState.pct || 0))}% coupon will be used instead of your 5% referral discount when it's a better deal.
          </div>
        )}
      </div>
      {(() => {
        if (plans === null) {
          return (
            <div className="card" style={{ padding: 24, textAlign: 'center', color: 'var(--fg-3)', marginBottom: 24 }}>
              <Spinner/> <span style={{ marginLeft: 8 }}>Loading plans…</span>
            </div>
          );
        }
        if (plans.length === 0) {
          return (
            <div className="card" style={{ padding: 24, textAlign: 'center', marginBottom: 24 }}>
              <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>No plans available right now.</div>
              <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>
                Admin: restart the server or run <code style={{ background: 'var(--bg-2)', padding: '2px 6px', borderRadius: 4 }}>npm run seed</code>.
              </div>
            </div>
          );
        }

        const bandwidth = plans.filter(p => !p.id.startsWith('mbps'));
        const speed = plans.filter(p => p.id.startsWith('mbps'));
        // Group speed plans by tier
        const tierMap = new Map();
        for (const p of speed) {
          const m = p.id.match(/^mbps(\d+)-(daily|weekly|monthly)$/);
          if (!m) continue;
          const mbps = Number(m[1]);
          if (!tierMap.has(mbps)) tierMap.set(mbps, {});
          tierMap.get(mbps)[m[2]] = p;
        }
        const tiers = [...tierMap.entries()].sort((a, b) => a[0] - b[0]);

        return (
          <>
            {/* Bandwidth cards */}
            {bandwidth.length > 0 && (
              <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.min(bandwidth.length, 2)}, 1fr)`, gap: 12, marginBottom: 20 }}>
                {bandwidth.map(p => {
                  const insufficient = data && data.balance_usd + 0.001 < p.price;
                  return (
                    <div key={p.id} className="card" style={{ padding: 20, border: p.popular ? '1px solid var(--accent)' : '1px solid var(--line)' }}>
                      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
                        <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{p.label}</div>
                        {p.popular && <span className="chip chip-accent" style={{ fontSize: 10 }}>Popular</span>}
                      </div>
                      <div style={{ fontSize: 18, fontWeight: 500, marginBottom: 4 }}>{p.name}</div>
                      <div className="mono" style={{ fontSize: 22, fontWeight: 500, letterSpacing: '-0.02em', marginBottom: 14 }}>
                        ${Number.isInteger(Number(p.price))
                          ? Number(p.price).toLocaleString('en-US')
                          : Number(p.price).toFixed(2)}
                      </div>
                      <button
                        className="btn btn-primary btn-sm"
                        onClick={() => requestBuy(p)}
                        disabled={buying === p.id || insufficient}
                        style={{ width: '100%', justifyContent: 'center', opacity: (buying === p.id || insufficient) ? 0.6 : 1 }}
                      >
                        {buying === p.id ? <><Spinner/> Activating…</> :
                         insufficient   ? 'Not enough balance' : 'Buy with balance'}
                      </button>
                    </div>
                  );
                })}
              </div>
            )}

            {/* Speed-tier cards */}
            {tiers.length > 0 && (
              <div style={{ marginBottom: 24 }}>
                <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 14 }}>
                  <div>
                    <div style={{ fontSize: 15, fontWeight: 500 }}>Dedicated speed lanes</div>
                    <div style={{ fontSize: 12, color: 'var(--fg-3)', marginTop: 2 }}>Unmetered bandwidth, fixed speed, chosen window.</div>
                  </div>
                  <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                    4 tiers · 3 durations
                  </div>
                </div>

                <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 14 }}>
                  {tiers.map(([mbps, dur], idx) => {
                    // Daily price for calculating per-duration savings
                    const daily = dur.daily ? Number(dur.daily.price) : null;
                    // "intensity" grows with tier — higher tiers feel more premium
                    const intensity = [0.08, 0.14, 0.22, 0.32][idx] ?? 0.15;
                    const isTop = mbps === 1000;

                    return (
                      <div
                        key={mbps}
                        className="card"
                        style={{
                          padding: 0,
                          overflow: 'hidden',
                          position: 'relative',
                          border: isTop
                            ? '1px solid color-mix(in oklab, var(--accent) 45%, var(--line))'
                            : '1px solid var(--line)',
                          background: `linear-gradient(135deg, color-mix(in oklab, var(--accent) ${intensity * 100}%, var(--bg-1)) 0%, var(--bg-1) 65%)`,
                        }}
                      >
                        {/* Decorative accent bar */}
                        <div style={{
                          position: 'absolute', top: 0, left: 0, right: 0, height: 2,
                          background: `linear-gradient(90deg, var(--accent) 0%, color-mix(in oklab, var(--accent) 30%, transparent) 100%)`,
                          opacity: 0.3 + intensity,
                        }}/>

                        {/* Tier header */}
                        <div style={{ padding: '22px 24px 16px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
                            <div className="mono" style={{ fontSize: 34, fontWeight: 500, letterSpacing: '-0.03em', lineHeight: 1 }}>
                              {mbps}
                            </div>
                            <div style={{ fontSize: 14, color: 'var(--fg-2)', fontWeight: 500 }}>Mbps</div>
                          </div>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                            {isTop && (
                              <span className="chip chip-accent" style={{ fontSize: 10 }}>
                                <Icon name="check" size={9}/> Top tier
                              </span>
                            )}
                            <span style={{ fontSize: 10, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
                              Residential · Unmetered
                            </span>
                          </div>
                        </div>

                        {/* Duration options */}
                        <div style={{
                          padding: '0 14px 14px', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8,
                        }}>
                          {['daily', 'weekly', 'monthly'].map(durKey => {
                            const p = dur[durKey];
                            if (!p) {
                              return (
                                <div key={durKey} style={{
                                  padding: '14px 10px', textAlign: 'center',
                                  border: '1px dashed var(--line)', borderRadius: 10,
                                  fontSize: 11, color: 'var(--fg-3)',
                                }}>—</div>
                              );
                            }

                            // Savings vs buying N daily passes
                            const durationDays = durKey === 'daily' ? 1 : durKey === 'weekly' ? 7 : 30;
                            const savingsPct = (daily && durKey !== 'daily')
                              ? Math.round((1 - Number(p.price) / (daily * durationDays)) * 100)
                              : 0;

                            const insufficient = data && data.balance_usd + 0.001 < p.price;
                            const isBuying = buying === p.id;
                            const shortfall = insufficient && data ? Math.max(0, p.price - data.balance_usd) : 0;

                            const durLabel = durKey.charAt(0).toUpperCase() + durKey.slice(1);

                            return (
                              <button
                                key={durKey}
                                onClick={() => !insufficient && !isBuying && requestBuy(p)}
                                disabled={isBuying || insufficient}
                                title={insufficient ? `Need $${shortfall.toFixed(2)} more` : `Buy ${p.name}`}
                                style={{
                                  cursor: insufficient ? 'not-allowed' : 'pointer',
                                  background: insufficient
                                    ? 'var(--bg-2)'
                                    : 'color-mix(in oklab, var(--bg-2) 60%, transparent)',
                                  border: '1px solid',
                                  borderColor: insufficient ? 'var(--line-soft)' : 'var(--line)',
                                  borderRadius: 10,
                                  padding: '12px 8px',
                                  display: 'flex', flexDirection: 'column', alignItems: 'stretch', gap: 4,
                                  position: 'relative',
                                  opacity: insufficient ? 0.55 : 1,
                                  transition: 'all 0.15s',
                                }}
                                onMouseEnter={e => {
                                  if (!insufficient && !isBuying) {
                                    e.currentTarget.style.borderColor = 'var(--accent)';
                                    e.currentTarget.style.background = 'color-mix(in oklab, var(--accent) 10%, var(--bg-2))';
                                  }
                                }}
                                onMouseLeave={e => {
                                  if (!insufficient && !isBuying) {
                                    e.currentTarget.style.borderColor = 'var(--line)';
                                    e.currentTarget.style.background = 'color-mix(in oklab, var(--bg-2) 60%, transparent)';
                                  }
                                }}
                              >
                                {savingsPct > 0 && !insufficient && (
                                  <div style={{
                                    position: 'absolute', top: -8, right: 8,
                                    fontSize: 9, fontWeight: 600, letterSpacing: '0.06em',
                                    background: 'var(--ok)', color: '#051',
                                    padding: '2px 6px', borderRadius: 10,
                                  }}>
                                    SAVE {savingsPct}%
                                  </div>
                                )}
                                <div style={{
                                  fontSize: 10, color: 'var(--fg-3)',
                                  textTransform: 'uppercase', letterSpacing: '0.08em',
                                  textAlign: 'left',
                                }}>
                                  {durLabel}
                                </div>
                                <div className="mono" style={{
                                  fontSize: 18, fontWeight: 500, textAlign: 'left',
                                  color: insufficient ? 'var(--fg-2)' : 'var(--fg-0)',
                                }}>
                                  ${Number(p.price).toLocaleString('en-US')}
                                </div>
                                <div style={{
                                  fontSize: 10, textAlign: 'left',
                                  color: isBuying ? 'var(--accent)' : insufficient ? 'var(--warn)' : 'var(--fg-3)',
                                  minHeight: 12,
                                }}>
                                  {isBuying ? (
                                    <><Spinner/> <span style={{ marginLeft: 4 }}>Activating…</span></>
                                  ) : insufficient ? (
                                    `+$${shortfall.toFixed(2)} needed`
                                  ) : (
                                    `${durationDays} ${durationDays === 1 ? 'day' : 'days'} unmetered`
                                  )}
                                </div>
                              </button>
                            );
                          })}
                        </div>
                      </div>
                    );
                  })}
                </div>
              </div>
            )}
          </>
        );
      })()}

      {/* Transactions */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '16px 24px', borderBottom: '1px solid var(--line)' }}>
          <div style={{ fontSize: 14, fontWeight: 500 }}>Transaction history</div>
        </div>
        {!data ? (
          <div style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
            <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
          </div>
        ) : data.transactions.length === 0 ? (
          <div style={{ padding: 48, textAlign: 'center' }}>
            <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>No transactions yet.</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>Top up to get started.</div>
          </div>
        ) : (
          <table style={{ width: '100%' }}>
            <thead>
              <tr style={{ background: 'var(--bg-2)', borderBottom: '1px solid var(--line)' }}>
                {['Date', 'Type', 'Note', 'Amount'].map(h => (
                  <th key={h} style={{ padding: '10px 24px', fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', fontWeight: 500, textAlign: 'left' }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {data.transactions.map(t => (
                <tr key={t.id} style={{ borderBottom: '1px solid var(--line-soft)' }}>
                  <td style={{ padding: '14px 24px', fontSize: 13, color: 'var(--fg-1)' }}>{fmtDate(t.created_at)}</td>
                  <td style={{ padding: '14px 24px', fontSize: 13 }}>
                    <span className="chip"><Icon name={kindIcon(t.kind)} size={10}/> {kindLabel(t.kind)}</span>
                  </td>
                  <td style={{ padding: '14px 24px', fontSize: 13, color: 'var(--fg-2)' }}>{t.note || '—'}</td>
                  <td style={{ padding: '14px 24px' }} className="mono">
                    <span style={{ fontSize: 13, color: t.amount_usd >= 0 ? 'var(--ok)' : 'var(--fg-0)' }}>
                      {t.amount_usd >= 0 ? '+' : '−'}${Math.abs(t.amount_usd).toFixed(2)}
                    </span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>

      {topupOpen && <TopupModal onClose={() => setTopupOpen(false)} onFinished={() => { setTopupOpen(false); load(); }}/>}

      {confirmPlan && (
        <ConfirmPurchaseModal
          plan={confirmPlan}
          balance={data?.balance_usd || 0}
          discount={effectiveDiscountFor(confirmPlan)}
          couponCode={couponState?.valid ? couponState.code : null}
          busy={buying === confirmPlan.id}
          onCancel={() => setConfirmPlan(null)}
          onConfirm={confirmBuy}
        />
      )}
    </div>
  );
}

function TopupModal({ onClose, onFinished }) {
  const [amount, setAmount] = React.useState(20);
  const [phase,  setPhase]  = React.useState('form');     // form | paying | paid
  const [order,  setOrder]  = React.useState(null);
  const [err,    setErr]    = React.useState(null);
  const [copied, setCopied] = React.useState(false);
  const pollRef = React.useRef(null);

  const presets = [10, 20, 50, 100, 200, 500];

  const createOrder = async (e) => {
    e?.preventDefault();
    if (amount < 5) { setErr('Minimum top-up is $5.'); return; }
    setErr(null);
    try {
      const o = await window.api('/api/checkout/topup', {
        method: 'POST',
        body: { amount_usd: Number(amount) },
      });
      setOrder(o);
      setPhase('paying');
      // If OxaPay returned a pay_link, open it in a new tab so the user can
      // complete payment on the OxaPay checkout page.
      if (o.mode === 'oxapay' && o.pay_link) {
        try { window.open(o.pay_link, '_blank', 'noopener,noreferrer'); } catch (_) {}
      }
    } catch (e) {
      setErr(window.apiErrorMessage(e));
    }
  };

  // Poll order status once we have one. In OxaPay mode, status updates come
  // in via webhook — we just poll our DB to notice when it changes.
  React.useEffect(() => {
    if (phase !== 'paying' || !order?.order_id) return;
    let stop = false;
    const tick = async () => {
      if (stop) return;
      try {
        const o = await window.api(`/api/checkout/orders/${order.order_id}`);
        if (o.status === 'paid')   { setPhase('paid'); return; }
        if (o.status === 'failed' || o.status === 'expired') {
          setErr('Payment expired. Please start a new top-up.');
          setPhase('form');
          return;
        }
      } catch (_) {}
      pollRef.current = setTimeout(tick, 2500);
    };
    tick();
    return () => { stop = true; if (pollRef.current) clearTimeout(pollRef.current); };
  }, [phase, order?.order_id]);

  const copyLink = async () => {
    if (!order) return;
    const text = order.pay_link || order.address;
    if (!text) return;
    try { await navigator.clipboard.writeText(text); } catch (_) {}
    setCopied(true);
    setTimeout(() => setCopied(false), 1200);
  };

  return (
    <Modal onClose={onClose} title={phase === 'paid' ? 'Top-up confirmed' : 'Top up balance'}>
      {phase === 'form' && (
        <form onSubmit={createOrder} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          <div>
            <label style={{ fontSize: 11, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.08em', display: 'block', marginBottom: 8 }}>Amount (USD)</label>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6,1fr)', gap: 6, marginBottom: 10 }}>
              {presets.map(p => (
                <button
                  key={p}
                  type="button"
                  onClick={() => setAmount(p)}
                  style={{
                    padding: '9px', fontSize: 12,
                    border: '1px solid', borderColor: amount == p ? 'var(--accent)' : 'var(--line)',
                    borderRadius: 6, cursor: 'pointer',
                    background: amount == p ? 'color-mix(in oklab, var(--accent) 14%, transparent)' : 'var(--bg-2)',
                    color: amount == p ? 'var(--accent)' : 'var(--fg-1)',
                  }}
                >${p}</button>
              ))}
            </div>
            <input
              type="number" min="5" max="10000" step="0.01"
              value={amount}
              onChange={e => setAmount(e.target.value)}
              style={{ width: '100%' }}
            />
          </div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', lineHeight: 1.6 }}>
            Pay with any supported cryptocurrency (USDT, BTC, ETH, LTC, TRX, and more). You'll be redirected to OxaPay to complete the payment, then sent back automatically.
          </div>
          {err && <div role="alert" style={{ fontSize: 12, color: 'var(--err)' }}>{err}</div>}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 4 }}>
            <button type="button" className="btn btn-ghost" onClick={onClose}>Cancel</button>
            <button type="submit" className="btn btn-primary">Create payment <Icon name="arrow_right" size={14}/></button>
          </div>
        </form>
      )}

      {phase === 'paying' && order && (
        <div>
          <div style={{ fontSize: 13, color: 'var(--fg-2)', marginBottom: 16 }}>
            Complete your <span className="mono" style={{ color: 'var(--fg-0)' }}>${Number(order.amount_usd).toFixed(2)}</span> payment on OxaPay. Your balance will be credited automatically once the transaction confirms on-chain.
          </div>

          {order.mode === 'oxapay' ? (
            order.pay_link ? (
              <>
                <a
                  href={order.pay_link}
                  target="_blank"
                  rel="noopener noreferrer"
                  className="btn btn-primary"
                  style={{ width: '100%', justifyContent: 'center', marginBottom: 12, textDecoration: 'none' }}
                >
                  Open OxaPay checkout <Icon name="external" size={13}/>
                </a>
                <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Or copy link</div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 14 }}>
                  <div className="mono" style={{
                    flex: 1, padding: '10px 12px', background: 'var(--bg-2)',
                    border: '1px solid var(--line)', borderRadius: 8, fontSize: 11,
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                  }}>{order.pay_link}</div>
                  <button className="btn btn-ghost btn-sm" onClick={copyLink} style={{ color: copied ? 'var(--ok)' : undefined }}>
                    <Icon name={copied ? 'check' : 'copy'} size={12}/>
                  </button>
                </div>
              </>
            ) : (
              <div role="alert" style={{
                padding: 14, marginBottom: 12,
                background: 'color-mix(in oklab, var(--err) 10%, transparent)',
                border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
                borderRadius: 8, fontSize: 12, color: 'var(--fg-1)', lineHeight: 1.5,
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6, color: 'var(--err)' }}>
                  <Icon name="alert" size={14}/>
                  <span style={{ fontWeight: 500 }}>Payment link missing</span>
                </div>
                OxaPay accepted the order but didn't return a payment link. Check the server logs for the raw OxaPay response, or contact support with order id <span className="mono" style={{ color: 'var(--accent)' }}>{order.order_id}</span>.
              </div>
            )
          ) : (
            <>
              <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 6 }}>Deposit address (dev/mock)</div>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 14 }}>
                <div className="mono" style={{
                  flex: 1, padding: '10px 12px', background: 'var(--bg-2)',
                  border: '1px solid var(--line)', borderRadius: 8, fontSize: 11,
                  overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                }}>{order.address}</div>
                <button className="btn btn-ghost btn-sm" onClick={copyLink} style={{ color: copied ? 'var(--ok)' : undefined }}>
                  <Icon name={copied ? 'check' : 'copy'} size={12}/>
                </button>
              </div>
            </>
          )}

          <div style={{
            padding: 14, background: 'color-mix(in oklab, var(--accent) 8%, transparent)',
            border: '1px solid color-mix(in oklab, var(--accent) 25%, transparent)',
            borderRadius: 8, display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12,
          }}>
            <Spinner/>
            <div style={{ fontSize: 12, color: 'var(--fg-1)' }}>
              {order.mode === 'oxapay'
                ? 'Waiting for payment confirmation from OxaPay…'
                : 'Waiting for payment confirmation… (mock: ~7 seconds)'}
            </div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', textAlign: 'center' }}>
            This window will update automatically once the payment is detected. You can safely close it — your balance will still be credited.
          </div>
        </div>
      )}

      {phase === 'paid' && (
        <div style={{ textAlign: 'center', padding: '20px 0' }}>
          <div style={{
            width: 56, height: 56, borderRadius: '50%',
            background: 'color-mix(in oklab, var(--ok) 20%, transparent)',
            display: 'grid', placeItems: 'center', margin: '0 auto 16px',
          }}>
            <Icon name="check" size={26} color="var(--ok)"/>
          </div>
          <div style={{ fontSize: 17, fontWeight: 500, marginBottom: 6 }}>
            ${Number(order.amount_usd).toFixed(2)} added to your balance
          </div>
          <div style={{ fontSize: 12, color: 'var(--fg-2)', marginBottom: 20 }}>
            You can now buy any plan instantly.
          </div>
          <button className="btn btn-primary" onClick={onFinished}>Done</button>
        </div>
      )}
    </Modal>
  );
}

function ConfirmPurchaseModal({ plan, balance, discount, couponCode, busy, onCancel, onConfirm }) {
  const listPrice = Number(plan.price);
  const discountAmount = Math.round(listPrice * (discount.pct / 100) * 100) / 100;
  const finalPrice = Math.round((listPrice - discountAmount) * 100) / 100;
  const balanceAfter = Math.round((balance - finalPrice) * 100) / 100;
  const insufficient = balance + 0.001 < finalPrice;

  // Whole numbers get comma separators (e.g. $1,200); otherwise show 2 decimals.
  // Keeps prices readable across both small ($15) and large ($1,200) tiers.
  const fmt = (n) => Number.isInteger(n)
    ? n.toLocaleString('en-US')
    : n.toFixed(2);

  return (
    <Modal onClose={busy ? () => {} : onCancel} title="Confirm purchase">
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{ padding: 16, background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 10 }}>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>
            You're buying
          </div>
          <div style={{ fontSize: 18, fontWeight: 500, marginBottom: 2 }}>{plan.name}</div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>{plan.gb}</div>
        </div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: '4px 4px' }}>
          <Row label="List price" value={`$${fmt(listPrice)}`} mono/>
          {discount.pct > 0 && (
            <Row
              label={discount.kind === 'coupon' ? `Coupon (${couponCode || ''})` : 'Referral discount'}
              value={`−$${fmt(discountAmount)} (${discount.pct.toFixed(discount.pct % 1 === 0 ? 0 : 1)}%)`}
              valueColor="var(--ok)"
              mono
            />
          )}
          <div style={{ height: 1, background: 'var(--line-soft)', margin: '4px 0' }}/>
          <Row label="You pay" value={`$${fmt(finalPrice)}`} mono valueSize={18} valueWeight={500}/>
        </div>

        <div style={{ padding: 12, background: 'var(--bg-2)', borderRadius: 8, fontSize: 12, color: 'var(--fg-2)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
            <span>Balance now</span>
            <span className="mono">${fmt(balance)}</span>
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', color: insufficient ? 'var(--err)' : 'var(--fg-1)' }}>
            <span>Balance after</span>
            <span className="mono">${fmt(balanceAfter)}</span>
          </div>
        </div>

        <div style={{ fontSize: 11, color: 'var(--fg-3)', lineHeight: 1.5 }}>
          A new endpoint will be issued for this plan. Other plans you've bought stay active and keep their own credentials.
        </div>

        <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
          <button type="button" className="btn btn-ghost" onClick={onCancel} disabled={busy}>Cancel</button>
          <button
            type="button"
            className="btn btn-primary"
            onClick={onConfirm}
            disabled={busy || insufficient}
          >
            {busy ? <><Spinner/> Activating…</> : insufficient ? 'Insufficient balance' : `Confirm — $${fmt(finalPrice)}`}
          </button>
        </div>
      </div>
    </Modal>
  );
}

function Row({ label, value, valueColor, mono, valueSize, valueWeight }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontSize: 13 }}>
      <span style={{ color: 'var(--fg-2)' }}>{label}</span>
      <span
        className={mono ? 'mono' : ''}
        style={{
          color: valueColor || 'var(--fg-0)',
          fontSize: valueSize || 13,
          fontWeight: valueWeight || 400,
        }}
      >{value}</span>
    </div>
  );
}

function Referrals() {
  const [data, setData] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const [copied, setCopied] = React.useState(null);

  const load = React.useCallback(async () => {
    try {
      const d = await window.api('/api/dashboard/referral');
      setData(d);
    } catch (e) {
      setErr(window.apiErrorMessage(e));
    }
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const shareLink = data?.code ? `${window.location.origin}/?ref=${data.code}` : '';

  const copy = async (text, key) => {
    try { await navigator.clipboard.writeText(text); } catch (_) {}
    setCopied(key);
    setTimeout(() => setCopied(c => (c === key ? null : c)), 1200);
  };

  const fmtDate = (iso) => {
    if (!iso) return '—';
    const d = new Date(iso + (iso.endsWith('Z') ? '' : 'Z'));
    return d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
  };

  if (err) {
    return (
      <div>
        <PageHeader title="Referrals"/>
        <div role="alert" style={{
          fontSize: 13, color: 'var(--err)',
          background: 'color-mix(in oklab, var(--err) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
          padding: '12px 16px', borderRadius: 8,
        }}>{err}</div>
      </div>
    );
  }

  if (!data) {
    return (
      <div>
        <PageHeader title="Referrals"/>
        <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)' }}>
          <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
        </div>
      </div>
    );
  }

  const discountPct = Math.round((data.discount_rate || 0) * 100);
  const rewardPct   = Math.round((data.reward_rate   || 0) * 100);

  return (
    <div>
      <PageHeader
        title="Referrals"
        subtitle={`Share your code — they save ${discountPct}% on every purchase, you earn ${rewardPct}% of every sale.`}
      />

      {data.am_referred && data.referrer && (
        <div style={{
          fontSize: 13, color: 'var(--accent)',
          background: 'color-mix(in oklab, var(--accent) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--accent) 30%, transparent)',
          padding: '11px 14px', borderRadius: 8, marginBottom: 20,
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <Icon name="check" size={13}/>
          You were referred by <span className="mono">{data.referrer.email}</span> — every plan is {discountPct}% off automatically.
        </div>
      )}

      {/* Stats row */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16, marginBottom: 24 }}>
        <div className="card" style={{ padding: 24 }}>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>People referred</div>
          <div className="mono" style={{ fontSize: 28, fontWeight: 500 }}>{data.referred_count}</div>
        </div>
        <div className="card" style={{ padding: 24 }}>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>Rewards earned</div>
          <div className="mono" style={{ fontSize: 28, fontWeight: 500 }}>{data.reward_count}</div>
        </div>
        <div className="card" style={{ padding: 24 }}>
          <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 8 }}>Total earned</div>
          <div className="mono" style={{ fontSize: 28, fontWeight: 500, color: 'var(--ok)' }}>
            ${Number(data.total_earned_usd).toFixed(2)}
          </div>
        </div>
      </div>

      {/* Code + share link */}
      <div className="card" style={{ padding: 28, marginBottom: 24 }}>
        <div style={{ fontSize: 15, fontWeight: 500, marginBottom: 18 }}>Your referral code</div>
        <div style={{ display: 'flex', gap: 10, marginBottom: 20 }}>
          <div className="mono" style={{
            flex: 1, padding: '14px 18px', background: 'var(--bg-2)',
            border: '1px solid var(--line)', borderRadius: 10,
            fontSize: 22, fontWeight: 500, letterSpacing: '0.1em',
            textAlign: 'center', color: 'var(--accent)',
          }}>
            {data.code}
          </div>
          <button className="btn btn-ghost" onClick={() => copy(data.code, 'code')} style={{ color: copied === 'code' ? 'var(--ok)' : undefined, padding: '0 18px' }}>
            <Icon name={copied === 'code' ? 'check' : 'copy'} size={14}/>
            {copied === 'code' ? 'Copied' : 'Copy code'}
          </button>
        </div>

        <div style={{ fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 6 }}>Or share this link</div>
        <div style={{ display: 'flex', gap: 10 }}>
          <div className="mono" style={{
            flex: 1, padding: '10px 14px', background: 'var(--bg-2)',
            border: '1px solid var(--line)', borderRadius: 8,
            fontSize: 12, color: 'var(--fg-1)',
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>
            {shareLink}
          </div>
          <button className="btn btn-ghost btn-sm" onClick={() => copy(shareLink, 'link')} style={{ color: copied === 'link' ? 'var(--ok)' : undefined }}>
            <Icon name={copied === 'link' ? 'check' : 'copy'} size={12}/>
          </button>
        </div>

        <div style={{ fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.6, marginTop: 20, padding: 14, background: 'var(--bg-2)', borderRadius: 8, border: '1px solid var(--line)' }}>
          <div style={{ marginBottom: 6, color: 'var(--fg-0)', fontWeight: 500 }}>How it works</div>
          Share this code with anyone. When they sign up with it, they get <b style={{ color: 'var(--accent)' }}>{discountPct}% off every plan for life</b>. You get <b style={{ color: 'var(--accent)' }}>{rewardPct}% of every sale</b> they make — credited straight to your balance.
        </div>
      </div>

      {/* Recent rewards table */}
      <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
        <div style={{ padding: '16px 24px', borderBottom: '1px solid var(--line)' }}>
          <div style={{ fontSize: 14, fontWeight: 500 }}>Reward history</div>
        </div>
        {data.recent_rewards.length === 0 ? (
          <div style={{ padding: 48, textAlign: 'center' }}>
            <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>No rewards yet.</div>
            <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>Share your code — you'll get credited the moment someone referred by you buys a plan.</div>
          </div>
        ) : (
          <table style={{ width: '100%' }}>
            <thead>
              <tr style={{ background: 'var(--bg-2)', borderBottom: '1px solid var(--line)' }}>
                {['Date', 'Referred user', 'Plan', 'Sale', 'Your reward'].map(h => (
                  <th key={h} style={{ padding: '10px 24px', fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', fontWeight: 500, textAlign: 'left' }}>{h}</th>
                ))}
              </tr>
            </thead>
            <tbody>
              {data.recent_rewards.map(r => (
                <tr key={r.id} style={{ borderBottom: '1px solid var(--line-soft)' }}>
                  <td style={{ padding: '14px 24px', fontSize: 13, color: 'var(--fg-1)' }}>{fmtDate(r.created_at)}</td>
                  <td style={{ padding: '14px 24px' }} className="mono">
                    <span style={{ fontSize: 12 }}>{r.referred_email}</span>
                  </td>
                  <td style={{ padding: '14px 24px', fontSize: 13 }}>
                    <span className="chip">{r.order_plan_id}</span>
                  </td>
                  <td style={{ padding: '14px 24px', fontSize: 13, color: 'var(--fg-2)' }} className="mono">
                    ${Number(r.sale_amount_usd).toFixed(2)}
                  </td>
                  <td style={{ padding: '14px 24px' }} className="mono">
                    <span style={{ fontSize: 13, color: 'var(--ok)' }}>+${Number(r.credit_amount_usd).toFixed(2)}</span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}

function Support() {
  const [tickets, setTickets] = React.useState(null);
  const [newOpen, setNewOpen] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [subject, setSubject] = React.useState('');
  const [body, setBody] = React.useState('');
  const [submitting, setSubmitting] = React.useState(false);
  const [openTicketId, setOpenTicketId] = React.useState(null);

  const load = React.useCallback(async () => {
    try {
      const data = await window.api('/api/dashboard/tickets');
      setTickets(data.tickets || []);
    } catch (err) {
      setError(window.apiErrorMessage(err));
      setTickets([]);
    }
  }, []);
  React.useEffect(() => { load(); }, [load]);

  const submit = async (e) => {
    e.preventDefault();
    setError(null);
    if (subject.trim().length < 3) {
      setError('Subject must be at least 3 characters.');
      return;
    }
    if (body.trim().length < 1) {
      setError('Please write a message describing your issue.');
      return;
    }
    setSubmitting(true);
    try {
      await window.api('/api/dashboard/tickets', { method: 'POST', body: { subject, body } });
      setSubject(''); setBody('');
      setNewOpen(false);
      await load();
    } catch (err) {
      setError(window.apiErrorMessage(err));
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div>
      <PageHeader
        title="Support"
        subtitle="We reply in minutes during business hours."
        actions={<button className="btn btn-primary btn-sm" onClick={() => setNewOpen(true)}><Icon name="plus" size={12}/> New ticket</button>}
      />

      {error && (
        <div role="alert" style={{
          fontSize: 12, color: 'var(--err)',
          background: 'color-mix(in oklab, var(--err) 10%, transparent)',
          border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
          padding: '9px 12px', borderRadius: 8, marginBottom: 16,
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <Icon name="alert" size={13}/> {error}
        </div>
      )}

      {tickets === null ? (
        <div className="card" style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
          <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
        </div>
      ) : tickets.length === 0 ? (
        <div className="card" style={{ padding: 48, textAlign: 'center' }}>
          <div style={{ fontSize: 14, color: 'var(--fg-1)', marginBottom: 6 }}>No tickets yet.</div>
          <div style={{ fontSize: 12, color: 'var(--fg-3)' }}>Click "New ticket" if you need a hand.</div>
        </div>
      ) : (
        <div className="card">
          {tickets.map((t, i) => (
            <button
              key={t.id}
              onClick={() => setOpenTicketId(t.id)}
              style={{
                display: 'flex', alignItems: 'center', gap: 16, padding: 20,
                borderTop: i > 0 ? '1px solid var(--line-soft)' : 'none',
                width: '100%', textAlign: 'left', background: 'transparent',
                cursor: 'pointer', transition: 'background 0.15s',
              }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-2)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
            >
              <span className="mono" style={{ fontSize: 12, color: 'var(--fg-2)', width: 60 }}>#{t.id}</span>
              <div style={{ flex: 1, fontSize: 14 }}>{t.subject}</div>
              <span className={t.status === 'open' ? 'chip chip-warn' : t.status === 'closed' ? 'chip chip-ok' : 'chip'}>{t.status}</span>
              <span style={{ fontSize: 12, color: 'var(--fg-3)', width: 80, textAlign: 'right' }}>{timeAgo(t.created_at)}</span>
              <Icon name="arrow_right" size={12} color="var(--fg-3)"/>
            </button>
          ))}
        </div>
      )}

      {newOpen && (
        <Modal onClose={() => setNewOpen(false)} title="New ticket">
          <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div>
              <label style={{ fontSize: 11, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.08em', display: 'block', marginBottom: 6 }}>Subject</label>
              <input value={subject} onChange={e => setSubject(e.target.value)} placeholder="Brief summary" maxLength={140} style={{ width: '100%' }} autoFocus/>
            </div>
            <div>
              <label style={{ fontSize: 11, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.08em', display: 'block', marginBottom: 6 }}>Message</label>
              <textarea value={body} onChange={e => setBody(e.target.value)} rows={6} placeholder="Tell us what's going on…" maxLength={4000} style={{ width: '100%', resize: 'vertical', fontFamily: 'inherit' }}/>
            </div>
            {error && (
              <div role="alert" style={{
                fontSize: 12, color: 'var(--err)',
                background: 'color-mix(in oklab, var(--err) 10%, transparent)',
                border: '1px solid color-mix(in oklab, var(--err) 30%, transparent)',
                padding: '9px 12px', borderRadius: 8,
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <Icon name="alert" size={13}/> {error}
              </div>
            )}
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 4 }}>
              <button type="button" className="btn btn-ghost" onClick={() => setNewOpen(false)} disabled={submitting}>Cancel</button>
              <button type="submit" className="btn btn-primary" disabled={submitting || !subject.trim() || !body.trim()}>
                {submitting ? <><Spinner/> Sending…</> : 'Send'}
              </button>
            </div>
          </form>
        </Modal>
      )}

      {openTicketId !== null && (
        <TicketThread
          ticketId={openTicketId}
          api="/api/dashboard/tickets"
          postAs="customer"
          onClose={() => { setOpenTicketId(null); load(); }}
        />
      )}
    </div>
  );
}

function timeAgo(iso) {
  if (!iso) return '';
  const diff = Date.now() - new Date(iso + (iso.endsWith('Z') ? '' : 'Z')).getTime();
  const s = Math.floor(diff / 1000);
  if (s < 60)    return `${s}s ago`;
  if (s < 3600)  return `${Math.floor(s / 60)}m ago`;
  if (s < 86400) return `${Math.floor(s / 3600)}h ago`;
  return `${Math.floor(s / 86400)}d ago`;
}

// Reusable ticket thread modal — used by both customer and admin support panels.
function TicketThread({ ticketId, api, postAs, onClose }) {
  const [data, setData] = React.useState(null);   // { ticket, messages }
  const [reply, setReply] = React.useState('');
  const [sending, setSending] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const listRef = React.useRef(null);

  const load = React.useCallback(async () => {
    try {
      const d = await window.api(`${api}/${ticketId}`);
      setData(d);
    } catch (e) {
      setErr(window.apiErrorMessage(e));
    }
  }, [api, ticketId]);
  React.useEffect(() => { load(); }, [load]);

  // Scroll to the bottom when new messages arrive
  React.useEffect(() => {
    if (listRef.current) listRef.current.scrollTop = listRef.current.scrollHeight;
  }, [data?.messages?.length]);

  const send = async (e) => {
    e.preventDefault();
    if (reply.trim().length < 1) return;
    setSending(true); setErr(null);
    try {
      await window.api(`${api}/${ticketId}/messages`, { method: 'POST', body: { body: reply } });
      setReply('');
      await load();
    } catch (e) {
      setErr(window.apiErrorMessage(e));
    } finally {
      setSending(false);
    }
  };

  return (
    <Modal onClose={onClose} title={data?.ticket ? `#${data.ticket.id} · ${data.ticket.subject}` : 'Ticket'}>
      {!data ? (
        <div style={{ padding: '24px 0', textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
          <Spinner/> <span style={{ marginLeft: 8 }}>Loading…</span>
        </div>
      ) : (
        <>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
            <span className={data.ticket.status === 'open' ? 'chip chip-warn' : data.ticket.status === 'closed' ? 'chip chip-ok' : 'chip'}>
              {data.ticket.status}
            </span>
            <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>Opened {timeAgo(data.ticket.created_at)}</span>
            {data.ticket.email && (
              <span style={{ fontSize: 12, color: 'var(--fg-3)', marginLeft: 'auto' }} className="mono">{data.ticket.email}</span>
            )}
          </div>

          <div ref={listRef} style={{
            maxHeight: 340, overflowY: 'auto',
            border: '1px solid var(--line)', borderRadius: 8,
            background: 'var(--bg-0)', padding: 14,
            display: 'flex', flexDirection: 'column', gap: 10,
          }}>
            {data.messages.map(m => (
              <div key={m.id} style={{
                alignSelf: m.author === postAs ? 'flex-end' : 'flex-start',
                maxWidth: '85%',
                background: m.author === postAs ? 'color-mix(in oklab, var(--accent) 18%, transparent)' : 'var(--bg-2)',
                border: '1px solid ' + (m.author === postAs ? 'color-mix(in oklab, var(--accent) 35%, transparent)' : 'var(--line)'),
                borderRadius: 10, padding: '9px 12px',
              }}>
                <div style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--fg-3)', marginBottom: 4 }}>
                  {m.author === 'admin' ? 'Support' : (m.author === postAs ? 'You' : 'You')}
                  {' · '}
                  {timeAgo(m.created_at)}
                </div>
                <div style={{ fontSize: 13, lineHeight: 1.5, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
                  {m.body}
                </div>
              </div>
            ))}
            {data.messages.length === 0 && (
              <div style={{ textAlign: 'center', fontSize: 12, color: 'var(--fg-3)', padding: 20 }}>No messages yet.</div>
            )}
          </div>

          {data.ticket.status !== 'closed' ? (
            <form onSubmit={send} style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 14 }}>
              <textarea
                value={reply}
                onChange={e => setReply(e.target.value)}
                rows={3}
                placeholder="Type your reply…"
                maxLength={4000}
                style={{ width: '100%', resize: 'vertical', fontFamily: 'inherit' }}
              />
              {err && (
                <div role="alert" style={{ fontSize: 12, color: 'var(--err)' }}>{err}</div>
              )}
              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
                <button type="button" className="btn btn-ghost" onClick={onClose}>Close</button>
                <button type="submit" className="btn btn-primary" disabled={sending || reply.trim().length === 0}>
                  {sending ? <><Spinner/> Sending…</> : <>Send reply <Icon name="arrow_right" size={12}/></>}
                </button>
              </div>
            </form>
          ) : (
            <div style={{ marginTop: 14, fontSize: 12, color: 'var(--fg-3)', textAlign: 'center' }}>
              This ticket is closed. Open a new one if you still need help.
            </div>
          )}
        </>
      )}
    </Modal>
  );
}

window.TicketThread = TicketThread;
window.__timeAgo = timeAgo;

window.Dashboard = Dashboard;
window.Sidebar = Sidebar;
window.TopBar = TopBar;
window.PageHeader = PageHeader;
