// Florio Analytics — Users tab.
// Surfaces three at-risk / churned cohorts so we can reach out:
//   1. Inactive > 7 days  — lastActiveDate older than a week (from users/{uid}).
//   2. Trial cancelled     — cancelled during the free trial
//                            (userSecure/{uid}.subscription.status === 'trial_cancelled').
//   3. Subscription cancelled — cancelled a paid subscription
//                            (subscription.status === 'cancelled').
// Data is joined client-side from `users` (name, email, lastActiveDate) and
// `userSecure` (subscription state). The subscription.status field is written
// going forward by the RevenueCat webhook and backfilled for existing users by
// the backfillSubscriptionStatus Cloud Function.
(function () {
  const I = window.HyperIcons;
  const { useState, useEffect, useMemo, useCallback } = React;

  const BACKFILL_URL = 'https://us-central1-florio-a6f48.cloudfunctions.net/backfillSubscriptionStatus';

  // ── Date helpers (lastActiveDate is a 'YYYY-MM-DD' local date string) ──
  const pad = (n) => String(n).padStart(2, '0');
  const todayStr = () => {
    const d = new Date();
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  };
  const daysAgoStr = (n) => {
    const d = new Date();
    d.setDate(d.getDate() - n);
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
  };
  const daysSince = (dateStr) => {
    if (!dateStr) return null;
    const then = new Date(dateStr + 'T00:00:00');
    if (isNaN(then.getTime())) return null;
    const now = new Date(todayStr() + 'T00:00:00');
    return Math.round((now - then) / 86400000);
  };
  const fmtMsDate = (ms) => {
    if (!ms) return null;
    const d = new Date(ms);
    return isNaN(d.getTime()) ? null : d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' });
  };

  const displayName = (u) => u.givenName || u.username || u.userName || '—';

  // ── Status → colored badge ──
  const STATUS_META = {
    trial_cancelled: { label: 'Trial cancelled', color: '#FE7E07' },
    cancelled: { label: 'Cancelled', color: '#FF5C7A' },
    expired: { label: 'Expired', color: '#9A8CFF' },
    billing_issue: { label: 'Billing issue', color: '#FF5C7A' },
    trial: { label: 'On trial', color: '#2ACF56' },
    active: { label: 'Active', color: '#2ACF56' },
    never: { label: 'Free', color: 'var(--text-muted)' },
  };
  function StatusBadge({ status }) {
    const meta = STATUS_META[status] || { label: status || 'Unknown', color: 'var(--text-muted)' };
    return (
      <span style={{
        fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 11.5, whiteSpace: 'nowrap',
        padding: '3px 9px', borderRadius: 99, color: meta.color,
        background: `color-mix(in srgb, ${meta.color} 14%, transparent)`,
      }}>{meta.label}</span>
    );
  }

  // ── Reusable rows table ──
  function UserTable({ rows, lastActiveCol }) {
    if (rows.length === 0) {
      return (
        <div style={{ minHeight: 120, display: 'grid', placeItems: 'center', textAlign: 'center', gap: 8, padding: 22 }}>
          <span style={{ color: 'var(--text-muted)', display: 'flex' }}>{I.users({ size: 24 })}</span>
          <span style={{ fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-muted)' }}>No users in this group</span>
        </div>
      );
    }
    const cellHead = { fontFamily: 'var(--font-mono-display)', fontSize: 10, letterSpacing: '.1em', textTransform: 'uppercase', color: 'var(--text-muted)', textAlign: 'left', padding: '0 12px 10px', fontWeight: 600 };
    const cell = { fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-secondary)', padding: '11px 12px', borderTop: '1px solid var(--border-subtle)', verticalAlign: 'middle' };
    return (
      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 540 }}>
          <thead>
            <tr>
              <th style={cellHead}>Name</th>
              <th style={cellHead}>Email</th>
              <th style={cellHead}>{lastActiveCol}</th>
              <th style={{ ...cellHead, textAlign: 'right' }}>Status</th>
            </tr>
          </thead>
          <tbody>
            {rows.map((u) => (
              <tr key={u.uid}>
                <td style={{ ...cell, color: 'var(--text-primary)', fontWeight: 600, whiteSpace: 'nowrap' }}>{displayName(u)}</td>
                <td style={{ ...cell, fontFamily: 'var(--font-mono)', fontSize: 12 }}>{u.email || '—'}</td>
                <td style={{ ...cell, whiteSpace: 'nowrap' }}>{u._when}</td>
                <td style={{ ...cell, textAlign: 'right' }}><StatusBadge status={u._status} /></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }

  function Users() {
    const P = window.PA;
    const [rows, setRows] = useState(null);
    const [err, setErr] = useState(null);
    const [q, setQ] = useState('');
    const [busy, setBusy] = useState(false);
    const [result, setResult] = useState(null);

    // Pull a generous cap; join users + their secure (subscription) doc.
    const loadRows = useCallback(async () => {
      const API = window.FlorioAPI;
      const [users, secure] = await Promise.all([
        API.fetchUsers(5000),
        API.fetchUserSecureDocs(5000),
      ]);
      const subByUid = {};
      secure.forEach((s) => { subByUid[s.uid] = (s.subscription) || {}; });
      return users.map((u) => ({ ...u, subscription: subByUid[u.uid] || {} }));
    }, []);

    useEffect(() => {
      let alive = true;
      (async () => {
        await window.FlorioReady;
        try {
          const merged = await loadRows();
          if (alive) setRows(merged);
        } catch (e) {
          console.error('[Users] load failed:', e);
          if (alive) { setErr(e.message || String(e)); setRows([]); }
        }
      })();
      return () => { alive = false; };
    }, [loadRows]);

    // Trigger the admin backfillSubscriptionStatus Cloud Function. `dryRun` caps
    // it to 50 users (?limit=50) so you can verify RevenueCat wiring safely first.
    const runBackfill = useCallback(async (dryRun) => {
      if (!dryRun && !window.confirm(
        'Backfill subscription status from RevenueCat for ALL users?\n\n' +
        'This calls the RevenueCat API once per user and can take a few minutes.',
      )) return;
      setBusy(true); setResult(null);
      try {
        const cur = window.FlorioAuth.auth.currentUser;
        if (!cur) throw new Error('Not signed in');
        const token = await cur.getIdToken();
        const res = await fetch(BACKFILL_URL + (dryRun ? '?limit=50' : ''), {
          headers: { Authorization: 'Bearer ' + token },
        });
        const text = await res.text();
        let json = null; try { json = JSON.parse(text); } catch (e) { /* non-JSON */ }
        if (!res.ok) throw new Error((json && json.error) || text || ('HTTP ' + res.status));
        setResult(Object.assign({ ok: true, dryRun }, json));
        try { setRows(await loadRows()); } catch (e) { /* keep old rows */ }
      } catch (e) {
        console.error('[Users] backfill failed:', e);
        setResult({ ok: false, error: e.message || String(e) });
      } finally {
        setBusy(false);
      }
    }, [loadRows]);

    const cohorts = useMemo(() => {
      const cutoff7 = daysAgoStr(7);
      const inactive = [];
      const trialCancelled = [];
      const subCancelled = [];
      const term = q.trim().toLowerCase();
      const matches = (u) => !term || displayName(u).toLowerCase().includes(term) || (u.email || '').toLowerCase().includes(term);

      (rows || []).forEach((u) => {
        const status = u.subscription?.status;

        if (matches(u) && u.lastActiveDate && u.lastActiveDate < cutoff7) {
          const d = daysSince(u.lastActiveDate);
          inactive.push({ ...u, _status: status || 'never', _when: d != null ? `${d} days ago` : u.lastActiveDate, _sort: d ?? 0 });
        }
        if (matches(u) && status === 'trial_cancelled') {
          trialCancelled.push({ ...u, _status: status, _when: fmtMsDate(u.subscription?.cancelledAt) || 'Unknown', _sort: u.subscription?.cancelledAt || 0 });
        }
        if (matches(u) && status === 'cancelled') {
          subCancelled.push({ ...u, _status: status, _when: fmtMsDate(u.subscription?.cancelledAt) || 'Unknown', _sort: u.subscription?.cancelledAt || 0 });
        }
      });

      inactive.sort((a, b) => b._sort - a._sort); // longest-inactive first
      trialCancelled.sort((a, b) => b._sort - a._sort); // most recent cancel first
      subCancelled.sort((a, b) => b._sort - a._sort);
      return { inactive, trialCancelled, subCancelled };
    }, [rows, q]);

    if (!rows) {
      return <div style={{ display: 'grid', placeItems: 'center', minHeight: '60vh' }}><div className="pa-spin" style={{ width: 38, height: 38, borderRadius: '50%', border: '3px solid var(--border-default)', borderTopColor: 'var(--app-accent)' }} /></div>;
    }

    const fmt = (n) => (window.FlorioAPI && window.FlorioAPI.formatNumber) ? window.FlorioAPI.formatNumber(n) : String(n ?? 0);

    return (
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(12, minmax(0,1fr))', gap: 20 }}>
        {err && (
          <div style={{ gridColumn: 'span 12', background: 'color-mix(in srgb, #FF5C7A 10%, transparent)', border: '1px solid #FF5C7A', borderRadius: 'var(--radius-lg)', padding: 14, fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-primary)' }}>
            Failed to load users: {err}
          </div>
        )}

        {/* KPI row */}
        <div style={{ gridColumn: 'span 12', display: 'grid', gridTemplateColumns: 'repeat(3,1fr)', gap: 16 }}>
          <P.StatTile icon={I.clock({ size: 18 })} label="Inactive > 7 days" value={fmt(cohorts.inactive.length)} accent="#9A8CFF" />
          <P.StatTile icon={I.droplet({ size: 18 })} label="Cancelled during trial" value={fmt(cohorts.trialCancelled.length)} accent="#FE7E07" />
          <P.StatTile icon={I.card({ size: 18 })} label="Cancelled subscription" value={fmt(cohorts.subCancelled.length)} accent="#FF5C7A" />
        </div>

        {/* Controls: search + RevenueCat backfill */}
        <div style={{ gridColumn: 'span 12', display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, background: 'var(--surface-card)', border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-md)', padding: '0 14px', height: 44, flex: 1, minWidth: 240, maxWidth: 380 }}>
            <span style={{ color: 'var(--text-muted)', display: 'flex' }}>{I.search({ size: 17 })}</span>
            <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Filter by name or email"
              style={{ border: 'none', outline: 'none', background: 'transparent', flex: 1, fontFamily: 'var(--font-sans)', fontSize: 14, color: 'var(--text-primary)' }} />
          </div>
          <div style={{ flex: 1 }} />
          <button onClick={() => runBackfill(true)} disabled={busy} title="Backfill just 50 users to verify the RevenueCat connection"
            style={{ height: 44, padding: '0 16px', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-default)', background: 'var(--surface-card)', color: 'var(--text-secondary)', cursor: busy ? 'default' : 'pointer', fontFamily: 'var(--font-sans)', fontWeight: 600, fontSize: 13.5, opacity: busy ? 0.5 : 1 }}>
            Test (50)
          </button>
          <button onClick={() => runBackfill(false)} disabled={busy}
            style={{ height: 44, padding: '0 18px', borderRadius: 'var(--radius-md)', border: 'none', background: 'var(--app-accent)', color: '#fff', cursor: busy ? 'default' : 'pointer', fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13.5, display: 'flex', alignItems: 'center', gap: 8, opacity: busy ? 0.7 : 1 }}>
            {busy
              ? <span className="pa-spin" style={{ width: 15, height: 15, borderRadius: '50%', border: '2px solid rgba(255,255,255,.5)', borderTopColor: '#fff', display: 'inline-block' }} />
              : I.sparkle({ size: 16 })}
            {busy ? 'Backfilling…' : 'Backfill from RevenueCat'}
          </button>
        </div>

        {result && (
          <div style={{ gridColumn: 'span 12', background: result.ok ? 'color-mix(in srgb, #2ACF56 12%, transparent)' : 'color-mix(in srgb, #FF5C7A 10%, transparent)', border: '1px solid ' + (result.ok ? '#2ACF56' : '#FF5C7A'), borderRadius: 'var(--radius-lg)', padding: 14, fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-primary)' }}>
            {result.ok
              ? (result.dryRun ? 'Dry run (up to 50 users): ' : 'Backfill complete: ') +
                `processed ${result.processed}, updated ${result.updated}, errors ${result.errors}.` +
                (result.statusCounts ? '  Statuses → ' + Object.entries(result.statusCounts).map(([k, v]) => `${k}: ${v}`).join(', ') : '')
              : 'Backfill failed: ' + result.error}
          </div>
        )}

        <P.ChartCard title="Inactive for more than 7 days" eyebrow={'(' + cohorts.inactive.length + ') haven’t opened the app in a week'} span={12}>
          <UserTable rows={cohorts.inactive} lastActiveCol="Last active" />
        </P.ChartCard>

        <P.ChartCard title="Cancelled during free trial" eyebrow={'(' + cohorts.trialCancelled.length + ') cancelled within the 7-day trial'} span={6}>
          <UserTable rows={cohorts.trialCancelled} lastActiveCol="Cancelled" />
        </P.ChartCard>

        <P.ChartCard title="Cancelled subscription" eyebrow={'(' + cohorts.subCancelled.length + ') cancelled a paid plan'} span={6}>
          <UserTable rows={cohorts.subCancelled} lastActiveCol="Cancelled" />
        </P.ChartCard>
      </div>
    );
  }

  (window.FlorioTabs = window.FlorioTabs || {}).users = Users;
})();
