// ╔══════════════════════════════════════════════════════════════════╗
// ║  backend.jsx — bridges the prototype UI to Supabase + the Worker.  ║
// ║  Exposes: window.sbClient, window.api, window.SweepifyBackend,     ║
// ║           window.StripeTopUpModal                                  ║
// ╚══════════════════════════════════════════════════════════════════╝

const CFG = window.SWEEPIFY_CONFIG;

// Supabase client (UMD global `supabase`). Persists session in localStorage,
// auto-refreshes, and detects the magic-link tokens in the URL on load.
const sbClient = supabase.createClient(CFG.SUPABASE_URL, CFG.SUPABASE_ANON_KEY, {
  auth: { persistSession: true, autoRefreshToken: true, detectSessionInUrl: true },
});

// ── API helpers (attach the user's JWT to Worker calls) ───────────────
async function authHeader() {
  const { data: { session } } = await sbClient.auth.getSession();
  return session ? { Authorization: `Bearer ${session.access_token}` } : {};
}

async function apiPost(path, body, withAuth = true) {
  const headers = { 'Content-Type': 'application/json', ...(withAuth ? await authHeader() : {}) };
  const res = await fetch(`${CFG.API_BASE}${path}`, {
    method: 'POST', headers, body: JSON.stringify(body || {}),
  });
  let data = null;
  try { data = await res.json(); } catch (_) {}
  if (!res.ok) {
    const err = new Error((data && data.error) || `Request failed (${res.status})`);
    err.status = res.status; err.data = data;
    throw err;
  }
  return data;
}

const api = {
  sendOtp:  (email) => apiPost('/api/auth/send-otp', { email }, false),
  scan:     (payload) => apiPost('/api/scan', payload),
  faceScan: (payload) => apiPost('/api/face-scan', payload),
  createPaymentIntent: (pack) => apiPost('/api/create-payment-intent', { pack }),
};

// ── DB → UI shape mappers ─────────────────────────────────────────────
const SCAN_TYPE_LABEL = {
  original_report_mpc: 'Original Report',
  original_report_sweepify: 'Original Report',
  person_check: 'Person Check',
  face_scan: 'Face Scan',
  dating_app_scan: 'Dating App Scan',
  tinder_sonar: 'Tinder Sonar',
  relationship_status: 'Relationship Status',
};
const STATUS_LABEL = { pending: 'Pending', processing: 'Processing', completed: 'Completed', failed: 'Failed' };
const NOTIF_TYPE_MAP = {
  report_ready: 'report', shield_activated: 'shield', shield_expiring: 'shield',
  tokens_credited: 'tokens', low_balance: 'alert', scan_failed: 'alert',
};

function initialsOf(name) {
  if (!name) return '–';
  const parts = name.trim().split(/\s+/);
  return ((parts[0]?.[0] || '') + (parts[1]?.[0] || '')).toUpperCase() || name[0].toUpperCase();
}
function fmtDate(iso) {
  try { return new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }
  catch (_) { return ''; }
}
function relTime(iso) {
  const d = new Date(iso), now = Date.now(), diff = (now - d.getTime()) / 1000;
  if (diff < 3600) return `${Math.max(1, Math.round(diff / 60))} min ago`;
  if (diff < 86400) return `${Math.round(diff / 3600)} hours ago`;
  if (diff < 172800) return 'Yesterday';
  return fmtDate(iso);
}

function mapSearch(row) {
  const label = SCAN_TYPE_LABEL[row.scan_type] || row.scan_type;
  const isOriginal = row.scan_type.startsWith('original_report');
  return {
    id: row.id,
    pinned: isOriginal,
    isOriginal,
    name: row.subject_name || 'Identity Report',
    handle: row.subject_identifier || '',
    initials: initialsOf(row.subject_name),
    color: (window.SCAN_NAME_ACCENT && window.SCAN_NAME_ACCENT[label]) || '#2563EB',
    scans: [label],
    date: fmtDate(row.created_at),
    status: STATUS_LABEL[row.status] || row.status,
    _raw: row,
  };
}
function mapNotif(row) {
  return {
    id: row.id,
    type: NOTIF_TYPE_MAP[row.type] || 'report',
    title: row.title || '',
    body: row.body || '',
    time: relTime(row.created_at),
    unread: !row.read,
    cta: row.action_url ? 'View report' : null,
    _raw: row,
  };
}

// ── Data bootstrap (RLS-scoped reads with the user's session) ─────────
const SweepifyBackend = {
  api,
  async getSession() { const { data } = await sbClient.auth.getSession(); return data.session; },
  async getUserEmail() { const { data } = await sbClient.auth.getUser(); return data.user?.email || null; },
  async signOut() { await sbClient.auth.signOut(); },

  // Loads everything the Dashboard needs in one shot.
  async load() {
    const [{ data: bal }, { data: searches }, { data: notifs }, { data: shields }] = await Promise.all([
      sbClient.from('dash_token_balances').select('balance').maybeSingle(),
      sbClient.from('dash_searches').select('*').order('created_at', { ascending: false }),
      sbClient.from('dash_notifications').select('*').order('created_at', { ascending: false }).limit(50),
      sbClient.from('dash_privacy_shields').select('*').eq('status', 'active').order('created_at', { ascending: false }).limit(1),
    ]);
    return {
      balance: bal ? Number(bal.balance) : 0,
      searches: (searches || []).map(mapSearch),
      notifications: (notifs || []).map(mapNotif),
      shield: (shields && shields[0]) ? {
        active: true, paused: shields[0].status === 'paused',
        plan: { shield_30: 'Shield 30', shield_plus: 'Shield+', shield_lifetime: 'Lifetime Shield' }[shields[0].tier] || 'Shield',
        expires: shields[0].expires_at ? fmtDate(shields[0].expires_at) : 'Never',
        remaining: shields[0].expires_at
          ? `${Math.max(0, Math.round((new Date(shields[0].expires_at) - Date.now()) / 86400000))} days`
          : 'Lifetime',
        autorenew: !!shields[0].auto_renew,
      } : null,
    };
  },

  async refreshBalance() {
    const { data } = await sbClient.from('dash_token_balances').select('balance').maybeSingle();
    return data ? Number(data.balance) : 0;
  },

  async markNotificationRead(id) {
    await sbClient.from('dash_notifications').update({ read: true }).eq('id', id);
  },
};

// ── Stripe Top-Up modal (Payment Element embedded, no redirect) ───────
function StripeTopUpModal({ open, pack, onClose, onSuccess }) {
  const { useState, useEffect, useRef } = React;
  const [status, setStatus] = useState('loading'); // loading | ready | paying | error
  const [error, setError] = useState(null);
  const elRef = useRef(null);
  const ctx = useRef({ stripe: null, elements: null });

  useEffect(() => {
    if (!open || !pack) return;
    let cancelled = false;
    setStatus('loading'); setError(null);
    (async () => {
      try {
        const { clientSecret, publishableKey } = await api.createPaymentIntent(pack.id);
        if (cancelled) return;
        const stripe = window.Stripe(publishableKey || CFG.STRIPE_PUBLISHABLE_KEY);
        const elements = stripe.elements({
          clientSecret,
          appearance: { theme: 'stripe', variables: { colorPrimary: '#2563EB', borderRadius: '10px' } },
        });
        ctx.current = { stripe, elements };
        setStatus('ready');
        // Mount after the element div is in the DOM.
        requestAnimationFrame(() => {
          if (cancelled || !elRef.current) return;
          elements.create('payment').mount(elRef.current);
        });
      } catch (e) {
        if (!cancelled) { setError(e.message || 'Could not start checkout.'); setStatus('error'); }
      }
    })();
    return () => { cancelled = true; };
  }, [open, pack]);

  const pay = async () => {
    const { stripe, elements } = ctx.current;
    if (!stripe || !elements) return;
    setStatus('paying'); setError(null);
    const { error: payErr } = await stripe.confirmPayment({ elements, redirect: 'if_required' });
    if (payErr) { setError(payErr.message); setStatus('ready'); return; }
    onSuccess && onSuccess(pack);
    onClose && onClose();
  };

  if (!open || !pack) return null;
  return (
    <Modal open={open} onClose={status === 'paying' ? () => {} : onClose} className="modal--sm" labelledBy="topup-pay-title">
      <button className="modal__close" onClick={onClose} aria-label="Close"><IconX size={20} /></button>
      <div style={{ padding: '4px 2px' }}>
        <h2 id="topup-pay-title" style={{ fontSize: 20, fontWeight: 800, margin: '0 0 4px' }}>
          {pack.name} pack
        </h2>
        <p style={{ color: 'var(--sw-fg-muted)', fontSize: 14, margin: '0 0 18px' }}>
          {pack.price} — {window.formatTokens(pack.total)} tokens credited instantly.
        </p>

        {status === 'loading' && <div style={{ padding: '32px 0', textAlign: 'center', color: 'var(--sw-fg-subtle)' }}>Loading secure checkout…</div>}
        {status === 'error' && <div className="gate__error" style={{ textAlign: 'center', marginBottom: 12 }}>{error}</div>}

        <div ref={elRef} style={{ display: status === 'loading' || status === 'error' ? 'none' : 'block' }} />

        {(status === 'ready' || status === 'paying') && (
          <>
            {error && <div className="gate__error" style={{ marginTop: 10 }}>{error}</div>}
            <button className="sw-btn sw-btn--lg" style={{ width: '100%', marginTop: 16 }}
                    disabled={status === 'paying'} onClick={pay}>
              {status === 'paying' ? 'Processing…' : `Pay ${pack.price}`}
            </button>
            <div className="topuppage__foot" style={{ justifyContent: 'center', width: '100%', marginTop: 12 }}>
              <IconLock size={12} /> Secured by Stripe
            </div>
          </>
        )}
      </div>
    </Modal>
  );
}

Object.assign(window, { sbClient, api, SweepifyBackend, StripeTopUpModal });
