/* global React, sbx */
// Payment methods (Hub phase 1) — the "card on file" gate.
//
// PRE-STRIPE: a saved card is a stub row in payment_methods; adding one
// calls the add_stub_card() RPC. The gate it powers is real product
// policy from day one: no waitlist join / seat accept without a card on
// file — and nobody is ever charged until their match is confirmed
// (post_charge writes simulated ledger rows server-side at match lock).
//
// Falls back to localStorage if the payments.sql migration hasn't run
// yet, so booking flows keep working on preview — same convention as
// tee_waitlist in courses-data.jsx.

const PM_LS = 'spp_stub_card';
const PM_STUB = { id: 'local-stub', label: 'Test card •••• 4242', brand: 'visa', last4: '4242' };

function pmLocalGet(userId) {
  try {
    const m = JSON.parse(localStorage.getItem(PM_LS) || '{}');
    return m[userId] ? PM_STUB : null;
  } catch (_) { return null; }
}
function pmLocalSet(userId) {
  try {
    const m = JSON.parse(localStorage.getItem(PM_LS) || '{}');
    m[userId] = true;
    localStorage.setItem(PM_LS, JSON.stringify(m));
  } catch (_) { /* private mode */ }
}

// undefined = loading · null = none on file · { id, label, brand, last4 }
function usePaymentMethod(userId) {
  const [method, setMethod] = React.useState(undefined);
  const [rev, setRev] = React.useState(0);

  React.useEffect(() => {
    if (!userId) { setMethod(null); return undefined; }
    let on = true;
    (async () => {
      const { data, error } = await sbx.from('payment_methods')
        .select('id, label, brand, last4')
        .eq('user_id', userId)
        .order('created_at', { ascending: true })
        .limit(1).maybeSingle();
      if (!on) return;
      // Table not migrated yet (or offline) → honor the on-device stub so
      // preview flows still work before the SQL runs.
      if (error) { setMethod(pmLocalGet(userId)); return; }
      setMethod(data || pmLocalGet(userId));
    })();
    return () => { on = false; };
  }, [userId, rev]);

  const refresh = React.useCallback(() => setRev(r => r + 1), []);
  return [method, refresh];
}

// Save the test card (idempotent server-side — returns the existing
// card's id when one is already on file).
async function addStubCard(userId) {
  const { data, error } = await sbx.rpc('add_stub_card');
  if (error) {
    // RPC not migrated yet → keep the gate working on-device.
    if (userId) { pmLocalSet(userId); return 'local-stub'; }
    throw new Error(error.message || 'Could not save the card.');
  }
  return data;
}

// ─── Card-on-file DISPLAY record (pre-Stripe) ─────────────────────────
// The `payment_methods` table + add_stub_card() RPC only carry the gate
// (brand/last4 default to a test card). To show the actual card the golfer
// typed in the new Payment Method screen — holder, brand, last4, expiry —
// we keep a lightweight display record on-device. Full PAN/CVC are NEVER
// stored anywhere (PCI): once Stripe is wired, the form's onSubmit hands the
// raw number straight to Stripe.js for tokenization and only the token +
// this same {brand,last4,holder,exp} shape comes back. Keyed per user.
const CARD_LS = 'spx_card_onfile';

function cardOnFileGet(userId) {
  if (!userId) return null;
  try {
    const m = JSON.parse(localStorage.getItem(CARD_LS) || '{}');
    return m[userId] || null;
  } catch (_) { return null; }
}
function cardOnFileSet(userId, card) {
  if (!userId) return;
  try {
    const m = JSON.parse(localStorage.getItem(CARD_LS) || '{}');
    m[userId] = { brand: card.brand, last4: card.last4, holder: card.holder, exp: card.exp };
    localStorage.setItem(CARD_LS, JSON.stringify(m));
  } catch (_) { /* private mode */ }
}
function cardOnFileClear(userId) {
  if (!userId) return;
  try {
    const m = JSON.parse(localStorage.getItem(CARD_LS) || '{}');
    delete m[userId];
    localStorage.setItem(CARD_LS, JSON.stringify(m));
  } catch (_) { /* private mode */ }
}

// ─── Credit balances (credit_ledger) ──────────────────────────────────
// The two-instrument architecture, summed for display:
//   · creditCents — refund credits (money paid, NEVER expires)
//   · couponCents — active promo coupons (30-day expiries)
// → { creditCents, couponCents, soonestExpiry } · null while loading.
function useCreditBalances(userId) {
  const [balances, setBalances] = React.useState(null);
  React.useEffect(() => {
    if (!userId) { setBalances({ creditCents: 0, couponCents: 0, soonestExpiry: null }); return undefined; }
    let on = true;
    (async () => {
      const { data, error } = await sbx.from('credit_ledger')
        .select('kind, amount_cents, expires_at')
        .eq('user_id', userId);
      if (!on) return;
      if (error) { setBalances({ creditCents: 0, couponCents: 0, soonestExpiry: null }); return; }
      const now = Date.now();
      let creditCents = 0, couponCents = 0, soonestExpiry = null;
      (data || []).forEach(r => {
        if (r.kind === 'refund_credit') creditCents += r.amount_cents;
        else if (r.kind === 'promo_coupon') {
          const exp = r.expires_at ? new Date(r.expires_at).getTime() : null;
          if (exp && exp <= now) return; // expired coupons don't count
          couponCents += r.amount_cents;
          if (r.amount_cents > 0 && exp && (!soonestExpiry || exp < soonestExpiry)) soonestExpiry = exp;
        }
      });
      setBalances({ creditCents: Math.max(0, creditCents), couponCents: Math.max(0, couponCents), soonestExpiry });
    })();
    return () => { on = false; };
  }, [userId]);
  return balances;
}

Object.assign(window, { usePaymentMethod, addStubCard, cardOnFileGet, cardOnFileSet, cardOnFileClear, useCreditBalances });
