/* global React, Icon, Button, usePaymentMethod, addStubCard, cardOnFileGet, cardOnFileSet, cardOnFileClear */

// ─── Payment Method screen ────────────────────────────────────────────
// Reached from Profile → "Payment method" (above Manage my membership).
// Golfers save the card that gets put on file. PRE-STRIPE: we capture the
// card client-side, keep a {brand,last4,holder,exp} display record on-device
// (see payments-data.jsx), and satisfy the server "card on file" gate via
// addStubCard(). The raw number/CVC never leave the form — once Stripe is
// wired, CreditCardForm.onSubmit hands the number to Stripe.js for
// tokenization and only the token + this same display shape comes back.

// ── card brand + validation helpers ──
function pmDigits(s) { return (s || '').replace(/\D+/g, ''); }

function pmBrand(num) {
  const n = pmDigits(num);
  if (/^4/.test(n)) return 'visa';
  if (/^(5[1-5]|2(2[2-9]|[3-6]\d|7[01]|720))/.test(n)) return 'mastercard';
  if (/^3[47]/.test(n)) return 'amex';
  if (/^(6011|64[4-9]|65|622)/.test(n)) return 'discover';
  return 'card';
}

const PM_BRAND = {
  visa:       { label: 'VISA',       len: 16, cvc: 3 },
  mastercard: { label: 'Mastercard', len: 16, cvc: 3 },
  amex:       { label: 'AMEX',       len: 15, cvc: 4 },
  discover:   { label: 'Discover',   len: 16, cvc: 3 },
  card:       { label: 'Card',       len: 16, cvc: 3 },
};

function pmGroups(brand) { return brand === 'amex' ? [4, 6, 5] : [4, 4, 4, 4]; }

function pmFormatInput(digits, brand) {
  const g = pmGroups(brand), out = []; let i = 0;
  for (const size of g) { if (i >= digits.length) break; out.push(digits.slice(i, i + size)); i += size; }
  return out.join(' ');
}

function pmLuhn(num) {
  const n = pmDigits(num);
  if (n.length < 12) return false;
  let sum = 0, alt = false;
  for (let i = n.length - 1; i >= 0; i--) {
    let d = +n[i];
    if (alt) { d *= 2; if (d > 9) d -= 9; }
    sum += d; alt = !alt;
  }
  return sum % 10 === 0;
}

function pmExpValid(mmYY) {
  const m = pmDigits(mmYY);
  if (m.length !== 4) return false;
  const mm = +m.slice(0, 2), yy = +m.slice(2);
  if (mm < 1 || mm > 12) return false;
  const now = new Date();
  const curYY = now.getFullYear() % 100, curMM = now.getMonth() + 1;
  if (yy < curYY || (yy === curYY && mm < curMM)) return false;
  return true;
}

// Group any string into brand-shaped chunks (works on digits or • chars).
function pmChunk(str, groups, sep) {
  const out = []; let i = 0;
  for (const size of groups) { out.push(str.slice(i, i + size)); i += size; }
  return out.filter(Boolean).join(sep);
}

// The number shown on the 3D card face. `mode`:
//   'type' → the digits as typed, with placeholder dots for the rest
//   'mask' → saved card: dots with only last4 shown
function pmCardNumberFace({ digits, brand, mode, last4 }) {
  const len = PM_BRAND[brand].len;
  let chars;
  if (mode === 'mask') {
    chars = '•'.repeat(Math.max(0, len - 4)) + (last4 || '••••');
  } else {
    const d = (digits || '').slice(0, len);
    chars = d + '•'.repeat(Math.max(0, len - d.length));
  }
  return pmChunk(chars, pmGroups(brand), '   ');
}

// ── The single-sided 3D card (SBX-branded, tilts with the pointer) ──
function PaymentCard3D({ digits, holder, exp, brand, mode, last4 }) {
  const tiltRef = React.useRef(null);
  const start = React.useRef({ down: false });
  const TILT = 12;

  function onDown(e) { start.current.down = true; const el = tiltRef.current; if (el) el.style.transition = 'none'; }
  function onMove(e) {
    const el = tiltRef.current; if (!el || !start.current.down) return;
    const r = el.getBoundingClientRect();
    const cx = e.clientX - r.left, cy = e.clientY - r.top;
    el.style.setProperty('--tx', `${((cy - r.height / 2) / (r.height / 2)) * -TILT}deg`);
    el.style.setProperty('--ty', `${((cx - r.width / 2) / (r.width / 2)) * TILT}deg`);
    el.style.setProperty('--mx', `${cx}px`);
    el.style.setProperty('--my', `${cy}px`);
  }
  function spring() {
    const el = tiltRef.current; if (!el) return;
    el.style.transition = 'transform 0.55s cubic-bezier(0.22,1,0.36,1)';
    el.style.setProperty('--tx', '0deg'); el.style.setProperty('--ty', '0deg');
  }
  function onUp() { if (start.current.down) { start.current.down = false; spring(); } }

  const brandLabel = PM_BRAND[brand].label;

  return (
    <div style={{ perspective: 900 }}>
      <div ref={tiltRef}
        onPointerDown={onDown} onPointerMove={onMove} onPointerUp={onUp} onPointerLeave={onUp}
        style={{
          transformStyle: 'preserve-3d', transition: 'transform 0.08s ease-out',
          transform: 'rotateX(var(--tx,0deg)) rotateY(var(--ty,0deg))',
          touchAction: 'none', cursor: 'grab',
        }}>
        <div style={{
          position: 'relative', width: '100%', aspectRatio: '1.586 / 1',
          borderRadius: 'var(--radius-card-lg)', overflow: 'hidden', boxSizing: 'border-box',
          background: 'linear-gradient(160deg, var(--forest-dark) 0%, var(--forest) 55%, var(--moss) 100%)',
          color: 'var(--cream)', boxShadow: '0 26px 54px rgba(14,28,19,0.42)',
          padding: '20px 22px', display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
        }}>
          <div className="grain" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}/>
          {/* specular highlight follows the pointer */}
          <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none',
            background: 'radial-gradient(320px circle at var(--mx,80%) var(--my,0%), rgba(234,226,206,0.22), transparent 60%)' }}/>

          {/* top row: SBX brand + card network */}
          <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <img src="assets/monogram-cream.svg" alt="Sandbox" style={{ height: 26, opacity: 0.95 }}/>
            <span style={{ fontFamily: 'var(--font-display)', fontSize: 16, letterSpacing: '0.04em', opacity: 0.95 }}>{brandLabel}</span>
          </div>

          {/* chip + number */}
          <div style={{ position: 'relative' }}>
            <div style={{ width: 38, height: 27, borderRadius: 6, marginBottom: 12,
              background: 'linear-gradient(135deg, #E8D9A0 0%, #C9B063 45%, #EADFB6 100%)',
              boxShadow: 'inset 0 0 0 1px rgba(255,255,255,0.25)' }}/>
            <div style={{ fontFamily: 'var(--font-mono)', fontSize: 'clamp(15px, 5.4vw, 21px)',
              letterSpacing: '0.08em', whiteSpace: 'nowrap' }}>
              {pmCardNumberFace({ digits, brand, mode, last4 })}
            </div>
          </div>

          {/* bottom row: holder + expiry */}
          <div style={{ position: 'relative', display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 12 }}>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 8, fontFamily: 'var(--font-mono)', letterSpacing: '0.14em', textTransform: 'uppercase', opacity: 0.6 }}>Card holder</div>
              <div style={{ fontSize: 13, fontWeight: 700, letterSpacing: '0.04em', textTransform: 'uppercase', marginTop: 3,
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {holder || 'YOUR NAME'}
              </div>
            </div>
            <div style={{ textAlign: 'right', flexShrink: 0 }}>
              <div style={{ fontSize: 8, fontFamily: 'var(--font-mono)', letterSpacing: '0.14em', textTransform: 'uppercase', opacity: 0.6 }}>Expires</div>
              <div style={{ fontSize: 13, fontFamily: 'var(--font-mono)', fontWeight: 700, letterSpacing: '0.08em', marginTop: 3 }}>
                {exp && pmDigits(exp).length >= 3 ? `${pmDigits(exp).slice(0, 2)}/${pmDigits(exp).slice(2, 4)}` : 'MM/YY'}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ── The card-entry form (live-updates the 3D card above it) ──
function CreditCardForm({ defaultHolder, onSubmit, submitting }) {
  const [number, setNumber] = React.useState('');       // formatted card number
  const [holder, setHolder] = React.useState(defaultHolder || '');
  const [exp, setExp] = React.useState('');
  const [cvc, setCvc] = React.useState('');
  const [touched, setTouched] = React.useState({});

  const brand = pmBrand(number);
  const meta = PM_BRAND[brand];
  const digits = pmDigits(number);

  const valid = {
    number: pmLuhn(number) && digits.length === meta.len,
    holder: holder.trim().length >= 2,
    exp: pmExpValid(exp),
    cvc: cvc.length === meta.cvc,
  };
  const allValid = valid.number && valid.holder && valid.exp && valid.cvc;

  function setNum(v) {
    const d = pmDigits(v).slice(0, PM_BRAND[pmBrand(v)].len);
    setNumber(pmFormatInput(d, pmBrand(v)));
  }
  function setExpiry(v) {
    let d = pmDigits(v).slice(0, 4);
    if (d.length >= 1 && +d[0] > 1) d = '0' + d;            // 3→03
    setExp(d.length > 2 ? `${d.slice(0, 2)}/${d.slice(2)}` : d);
  }

  function blur(k) { setTouched(t => ({ ...t, [k]: true })); }
  function submit() {
    setTouched({ number: true, holder: true, exp: true, cvc: true });
    if (!allValid || submitting) return;
    onSubmit({ brand, last4: digits.slice(-4), holder: holder.trim(), exp: `${pmDigits(exp).slice(0, 2)}/${pmDigits(exp).slice(2, 4)}` });
  }

  const field = { marginBottom: 14 };
  const label = { fontSize: 11, fontFamily: 'var(--font-mono)', letterSpacing: '0.1em', textTransform: 'uppercase',
    color: 'var(--forest)', opacity: 0.7, marginBottom: 6, display: 'block' };
  const input = (bad) => ({
    width: '100%', boxSizing: 'border-box', padding: '13px 14px', borderRadius: 13,
    border: `1px solid ${bad ? 'rgba(178,59,43,0.6)' : 'rgba(14,28,19,0.14)'}`,
    background: 'var(--paper)', color: 'var(--ink)', fontSize: 15, fontFamily: 'var(--font-body)', outline: 'none',
  });

  return (
    <div>
      {/* live preview */}
      <PaymentCard3D digits={digits} holder={holder} exp={exp} brand={brand} mode="type"/>

      {/* A real <form> with name/autocomplete hints so iOS Safari (and other
          browsers) offer credit-card autofill for each field. */}
      <form style={{ marginTop: 22 }} onSubmit={e => { e.preventDefault(); submit(); }}>
        <div style={field}>
          <label style={label}>Card number</label>
          <input value={number} onChange={e => setNum(e.target.value)} onBlur={() => blur('number')}
            type="text" inputMode="numeric" name="cardnumber" autoComplete="cc-number" placeholder="1234 5678 9012 3456"
            style={{ ...input(touched.number && !valid.number), fontFamily: 'var(--font-mono)', letterSpacing: '0.06em' }}/>
        </div>

        <div style={field}>
          <label style={label}>Name on card</label>
          <input value={holder} onChange={e => setHolder(e.target.value)} onBlur={() => blur('holder')}
            type="text" name="ccname" autoComplete="cc-name" placeholder="Rob Piacenti"
            style={input(touched.holder && !valid.holder)}/>
        </div>

        <div style={{ display: 'flex', gap: 12 }}>
          <div style={{ ...field, flex: 1 }}>
            <label style={label}>Expiry</label>
            <input value={exp} onChange={e => setExpiry(e.target.value)} onBlur={() => blur('exp')}
              type="text" inputMode="numeric" name="cc-exp" autoComplete="cc-exp" placeholder="MM/YY"
              style={{ ...input(touched.exp && !valid.exp), fontFamily: 'var(--font-mono)' }}/>
          </div>
          <div style={{ ...field, flex: 1 }}>
            <label style={label}>CVC</label>
            <input value={cvc} onChange={e => setCvc(pmDigits(e.target.value).slice(0, meta.cvc))} onBlur={() => blur('cvc')}
              type="text" inputMode="numeric" name="cvc" autoComplete="cc-csc" placeholder={meta.cvc === 4 ? '1234' : '123'}
              style={{ ...input(touched.cvc && !valid.cvc), fontFamily: 'var(--font-mono)' }}/>
          </div>
        </div>

        {/* the promise — consistent with PaymentGateSheet */}
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, margin: '4px 0 18px' }}>
          <span style={{ width: 8, height: 8, borderRadius: 999, background: 'var(--forest)', flexShrink: 0, marginTop: 5 }}/>
          <div style={{ fontSize: 12, opacity: 0.7, lineHeight: 1.5 }}>
            You'll never be charged until your match is confirmed. Your card details are stored securely and never touch our servers in beta.
          </div>
        </div>

        <Button variant="forest" size="lg" full type="button" onClick={submit} disabled={submitting || !allValid}>
          {submitting ? 'Saving…' : 'Save card'}
        </Button>
      </form>
    </div>
  );
}

// ── The screen ──
function PaymentMethodScreen({ go, profile, onClose, embedded }) {
  const userId = profile && profile.id;
  const dismiss = onClose || (() => go({ overlay: undefined }));
  const [method, refreshMethod] = usePaymentMethod(userId);   // undefined=loading · null=none · {brand,last4}
  const [local, setLocal] = React.useState(() => cardOnFileGet(userId));
  const [editing, setEditing] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState('');

  // Slide the whole screen up on open, back down on close (then navigate).
  // Embedded in the settings drawer, the PARENT panel owns the horizontal
  // slide — skip the vertical animation and dismiss immediately.
  const [shown, setShown] = React.useState(!!embedded);
  const [closing, setClosing] = React.useState(false);
  React.useEffect(() => {
    if (embedded) return undefined;
    const id = requestAnimationFrame(() => setShown(true));
    return () => cancelAnimationFrame(id);
  }, []);
  function close() {
    if (embedded) { dismiss(); return; }
    if (closing) return;
    setClosing(true);
    setTimeout(dismiss, 340);
  }

  React.useEffect(() => { setLocal(cardOnFileGet(userId)); }, [userId]);

  const loading = method === undefined;
  const onFile = local || (method ? { brand: method.brand, last4: method.last4, holder: '', exp: '' } : null);
  const showForm = editing || (!loading && !onFile);

  async function handleSubmit(card) {
    setBusy(true); setErr('');
    try {
      await addStubCard(userId);            // satisfy the server card-on-file gate
      cardOnFileSet(userId, card);          // rich display record (pre-Stripe)
      setLocal(card);
      refreshMethod();
      setEditing(false);
    } catch (e) {
      setErr(e.message || 'Could not save your card.');
    }
    setBusy(false);
  }

  const defaultHolder = (profile && (profile.name || profile.full_name || profile.display_name)) || '';

  return (
    <div style={{
      minHeight: '100%', background: 'var(--cream)', paddingBottom: 60,
      transform: (shown && !closing) ? 'translateY(0)' : 'translateY(100%)',
      transition: 'transform 0.34s cubic-bezier(0.2,0.8,0.2,1)', willChange: 'transform',
    }}>
      {/* header */}
      <div style={{ padding: '54px 18px 8px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <button onClick={close} style={{
          width: 40, height: 40, borderRadius: 999, background: 'var(--paper)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: '1px solid rgba(14,28,19,0.1)', boxShadow: 'var(--shadow-md)',
        }}>
          <Icon.Chevron dir="left" size={13} color="var(--forest)"/>
        </button>
        <div style={{ fontSize: 10, fontFamily: 'var(--font-mono)', color: 'var(--forest)', opacity: 0.6, letterSpacing: '0.14em', textTransform: 'uppercase' }}>Payment Method</div>
        <div style={{ width: 40 }}/>
      </div>

      <div style={{ padding: '10px 22px 0' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 34, lineHeight: 1.0, letterSpacing: '-0.02em', color: 'var(--forest)' }}>
          {showForm ? 'What card do you\nwant on file?'.split('\n').map((l, i) => <div key={i}>{l}</div>) : 'Your card\non file'.split('\n').map((l, i) => <div key={i}>{l}</div>)}
        </div>
        <div className="caption-serif" style={{ fontSize: 15, opacity: 0.7, marginTop: 12, maxWidth: 340 }}>
          {showForm
            ? 'This is the card we put on file so we can lock your tee time the instant a match forms.'
            : 'This is the card on file for your matches. You won\'t be charged until a foursome locks in.'}
        </div>
      </div>

      <div style={{ padding: '22px 22px 0' }}>
        {loading ? (
          <div style={{ textAlign: 'center', opacity: 0.5, padding: '40px 0', fontSize: 14 }}>Loading…</div>
        ) : showForm ? (
          <>
            <CreditCardForm defaultHolder={defaultHolder} onSubmit={handleSubmit} submitting={busy}/>
            {err && <div style={{ fontSize: 13, color: 'var(--loss, #B23B2B)', fontWeight: 600, marginTop: 12, textAlign: 'center' }}>{err}</div>}
            {onFile && (
              <button onClick={() => { setEditing(false); setErr(''); }} style={{
                marginTop: 12, width: '100%', padding: '10px 0', background: 'transparent', border: 'none',
                color: 'var(--forest)', fontSize: 12, fontWeight: 700, cursor: 'pointer', opacity: 0.6,
              }}>Cancel</button>
            )}
          </>
        ) : (
          <>
            <PaymentCard3D
              brand={onFile.brand || 'card'}
              last4={onFile.last4}
              holder={onFile.holder}
              exp={onFile.exp}
              mode="mask"/>
            <div style={{ marginTop: 22 }}>
              <Button variant="outline" size="lg" full onClick={() => { setErr(''); setEditing(true); }}>
                Change my payment method
              </Button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { PaymentMethodScreen, CreditCardForm, PaymentCard3D });
