// web/account-v2.jsx — Account flow v4 max depth
// Tabs (Login | Signup), social login mock, validations, error states

const { useState: avS, useEffect: avE, useRef: avR } = React;

// [PHONE-FR 2026-07-07] Format a French number for INTERNATIONAL display: strip a leading
// "+33"/"33" and the national trunk "0" so "06 12 34 56 78" → "+33 6 12 34 56 78" (no more the
// malformed "+33 06 …" double-zero echoed on the OTP screen). Spacing is preserved.
function frPhoneIntl(raw) {
  const local = String(raw || '').trim().replace(/^\+?33\s*/, '').replace(/^0/, '');
  return '+33 ' + local;
}

const AVIcons = {
  google: <svg width="18" height="18" viewBox="0 0 24 24"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>,
  apple: <svg width="18" height="20" viewBox="0 0 384 512" fill="#0A0A0A"><path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9z"/></svg>,
  fb: <svg width="18" height="18" viewBox="0 0 24 24" fill="#1877F2"><path d="M24 12.07C24 5.41 18.63 0 12 0S0 5.4 0 12.07C0 18.1 4.39 23.1 10.13 24v-8.44H7.08v-3.49h3.04V9.41c0-3.02 1.8-4.7 4.54-4.7 1.31 0 2.68.24 2.68.24v2.97h-1.5c-1.5 0-1.96.93-1.96 1.89v2.26h3.32l-.53 3.5h-2.8V24C19.62 23.1 24 18.1 24 12.07"/></svg>,
};

function AccountFlow({ open, onClose, onAuthed }) {
  const [mode, setMode] = avS('login'); // 'login' | 'signup' | 'otp' | 'success' — [W-FN-1 heal 2026-06-08] header button is "Se connecter" → open LOGIN, not the registration tab (in-modal toggle switches to signup).
  const [form, setForm] = avS({ first: '', email: '', phone: '06 ', password: '' });
  const [errors, setErrors] = avS({});
  const [code, setCode] = avS(['','','','']);
  const [otpError, setOtpError] = avS(false);
  // [W16 DEV-OTP 2026-07-20] Code OTP renvoyé par le backend UNIQUEMENT hors production quand le SMS
  // n'est pas câblé (staging). Affiché sous les cases seulement si window.LC.isDev ; vide/absent en prod.
  const [devCode, setDevCode] = avS('');
  const refs = [avR(), avR(), avR(), avR()];
  const [loading, setLoading] = avS(false);
  const [resendIn, setResendIn] = avS(0);
  // [LOGIN-FEEDBACK 2026-07-07] Guests authenticate by phone+SMS (no email/password backend). The
  // "Se connecter" tab used to silently jump to the phone/signup form with no explanation. We now
  // surface a short notice so the switch is understood instead of feeling like a dead-end.
  const [authNotice, setAuthNotice] = avS('');

  avE(() => { if (!open) { setMode('login'); setCode(['','','','']); setOtpError(false); setErrors({}); setResendIn(0); setAuthNotice(''); setDevCode(''); } }, [open]); // [W-FN-1 heal 2026-06-08] reset-on-close defaults to LOGIN (header CTA is "Se connecter"); was 'signup' which forced the registration tab on every open.
  avE(() => { if (open && mode === 'otp') setTimeout(()=>refs[0].current?.focus(), 100); }, [mode, open]);
  // Start the resend countdown when entering OTP step
  avE(() => { if (open && mode === 'otp') setResendIn(29); }, [mode, open]);
  // Self-cleaning per-tick timer — avoids interval stacking
  avE(() => {
    if (resendIn <= 0) return;
    const t = setTimeout(() => setResendIn(s => s - 1), 1000);
    return () => clearTimeout(t);
  }, [resendIn]);

  const validate = () => {
    const e = {};
    if (mode === 'signup' && !form.first.trim()) e.first = 'Prénom requis';
    // [OWNER 2026-07-28 · GOAL WEB Wave C] Le code de vérification part par EMAIL (0 coût SMS)
    // → l'email est requis dans les DEUX modes (login + signup), c'est lui qui reçoit le code.
    if ((mode === 'signup' || mode === 'login') && (!form.email.includes('@') || !form.email.includes('.'))) e.email = 'Email invalide';
    if ((mode === 'signup' || mode === 'login') && form.phone.replace(/\s/g,'').length < 9) e.phone = 'Numéro français invalide';
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  // [WEB-WIREUP 2026-06-26] Real auth = phone + OTP (guest-signup). The backend has no guest
  // email/password path, so "Connexion" routes to the phone flow; "Inscription" sends a real SMS code.
  const api = (typeof window !== 'undefined' && window.LC && window.LC.api) || null;
  // [HEAL 2026-07-19] Taux de gain fidélité lu depuis window.LC.loyalty.config (earn_ratio, resynchronisé
  // du backend points_per_euro) au lieu d'un « 1 € = 1 point » figé. Repli sûr si la config est absente.
  const lcLoyaltyCfg = (typeof window !== 'undefined' && window.LC && window.LC.loyalty && window.LC.loyalty.config) || null;
  const earnRatioRaw = lcLoyaltyCfg ? (lcLoyaltyCfg.earn_ratio != null ? lcLoyaltyCfg.earn_ratio : lcLoyaltyCfg.points_per_euro) : null;
  const earnRatio = Number(earnRatioRaw);
  const earnLabel = (isFinite(earnRatio) && earnRatio > 0)
    ? ('1 € = ' + earnRatio + ' point' + (earnRatio > 1 ? 's' : ''))
    : '1 € = 10 points';
  const submit = async () => {
    // [TERRAIN-HEAL 2026-07-16 · WEB-LOGIN-DEADFIELDS] Login = téléphone + SMS (aucun backend
    // email/password). AVANT : le mode 'login' affichait des champs email/password MORTS puis, au
    // clic, jetait tout et basculait en 'signup' avec un message → UX trompeuse. Désormais le login
    // collecte directement le téléphone et envoie le code OTP (même flux que signup, sans prénom/email).
    setErrors({});
    if (!validate()) return;
    const phone = form.phone.replace(/\D/g, '');
    if (phone.length < 6) { setErrors(e => ({ ...e, phone: 'Numéro français invalide' })); return; }
    if (!api) { setErrors(e => ({ ...e, phone: 'API indisponible.' })); return; }
    setLoading(true);
    try {
      // [OWNER 2026-07-28 · Wave C] Canal EMAIL-OTP (le SMS n'a jamais été câblé → signup mort
      // en prod). Le backend envoie le code à form.email et le lie au téléphone (clé fidélité).
      const r = await api.guestEmailOtp(phone, form.email.trim());
      // Défensif : dev_code n'existe QUE hors prod SMS-off ; undefined en prod → hint jamais rendu.
      setDevCode(r && r.dev_code ? String(r.dev_code) : '');
      setLoading(false);
      setMode('otp');
    } catch (e) {
      setLoading(false);
      setErrors(er => ({ ...er, phone: (e && e.message) || 'Envoi du code impossible.' }));
    }
  };

  const onKey = (i, val) => {
    if (val && !/^\d$/.test(val)) return;
    if (otpError) setOtpError(false);
    const next = [...code]; next[i] = val; setCode(next);
    if (val && i < 3) refs[i+1].current?.focus();
    // [WEB-WIREUP 2026-06-26] Verify the SMS code against the real backend (guest-signup/verify).
    // Success → real Sanctum token stored (window.LC.api) → onAuthed. Wrong code → recoverable error.
    if (next.join('').length === 4) {
      const phone = form.phone.replace(/\D/g, '');
      const otp = next.join('');
      if (api) {
        api.guestVerify(phone, otp)
          // [HEAL 2026-07-19] Un 2xx ne prouve pas l'auth : api.js ne stocke le token QUE si r.token.
          // On ne bascule sur 'success' (→ onAuthed) que si isAuthed() est vrai ; sinon on montre l'erreur.
          .then(() => {
            if (api.isAuthed && api.isAuthed()) { setMode('success'); }
            else { setOtpError(true); setCode(['', '', '', '']); refs[0].current && refs[0].current.focus(); }
          })
          .catch(() => { setOtpError(true); setCode(['', '', '', '']); refs[0].current && refs[0].current.focus(); });
      } else {
        setOtpError(true);
      }
    }
  };

  // [AUDIT 2026-07-14] Connexion Google/Apple retirée : OAuth social non branché (backend
  //   POS local ne gère pas Google/Apple) → boutons non fonctionnels. À réintroduire le jour
  //   où un vrai fournisseur OAuth est câblé.
  const Social = () => null;

  return (
    <WebModal open={open} onClose={onClose} full label="Mon compte Le Cayenne">
      {(mode === 'login' || mode === 'signup') && (
        <div className="lc-acc">
          <div className="lc-acc-art">
            <div className="lc-acc-art-bg"/>
            <div className="lc-acc-art-stamp"><b>1€</b><i>1 PT</i></div>
            <div className="lc-acc-art-wordmark">
              <div className="le">LE</div>
              <div className="cay">Cayenne</div>
              <div className="lc-acc-art-tag">HÉNIN-BEAUMONT · 62110</div>
            </div>
          </div>
          <div className="lc-acc-form">
            {/* [WADV-5 heal 2026-06-10] role=tab + aria-selected so SR announces the active tab. */}
            <div className="lcf-tabs" role="tablist" aria-label="Connexion ou inscription">
              <button type="button" role="tab" aria-selected={mode==='login'} onClick={()=>{ setMode('login'); setAuthNotice(''); }} className={'lcf-tab' + (mode==='login'?' is-on':'')}>Connexion</button>
              <button type="button" role="tab" aria-selected={mode==='signup'} onClick={()=>{ setMode('signup'); setAuthNotice(''); }} className={'lcf-tab' + (mode==='signup'?' is-on':'')}>Inscription</button>
            </div>
            {/* [LOGIN-FEEDBACK 2026-07-07] Explains the phone/SMS switch after « Se connecter ». */}
            {mode === 'signup' && authNotice && (
              <div role="status" aria-live="polite" style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 12, padding: '10px 12px', borderRadius: 10, background: 'var(--cream-2, #f3efe6)', color: 'var(--gray-4, #555)', fontSize: 12.5, fontWeight: 600 }}>
                <WC_I.shield s={14}/> <span>{authNotice}</span>
              </div>
            )}
            <div className="lc-acc-eyebrow">// {mode === 'login' ? 'Bon retour' : 'Nouveau ici ?'}</div>
            <h1 className="lc-acc-title">{mode === 'login' ? <>Salut,<br/><span>chef.</span></> : <>Bienvenue,<br/><span>chef.</span></>}</h1>
            <p className="lc-acc-sub">{mode === 'login' ? 'Connecte-toi pour retrouver tes points, ton historique et tes récompenses.' : 'Crée ton compte en 30 sec. Pas de spam, juste les news qui comptent.'}</p>

            <Social/>

            <div className="lc-acc-fields">
              {mode === 'signup' && (
                <div className="lc-acc-field">
                  <label className="lc-acc-label" htmlFor="acc-first">Prénom</label>
                  <input id="acc-first" className={'lc-acc-input' + (errors.first?' lcf-acc-input--error':'')} value={form.first} onChange={e=>setForm(f=>({...f, first: e.target.value}))} placeholder="Ton prénom"/>{/* [GOAL-SYNC 2026-07-08] placeholder neutre (plus de persona démo) */}
                  {errors.first && <div className="lcf-field-error"><WC_I.close s={12}/> {errors.first}</div>}
                </div>
              )}
              {/* [OWNER 2026-07-28 · Wave C] Email requis dans les DEUX modes : c'est LUI qui
                  reçoit le code de vérification (canal email, 0 coût SMS). */}
              {(mode === 'login' || mode === 'signup') && (
                <div className="lc-acc-field">
                  <label className="lc-acc-label" htmlFor="acc-email">Email</label>
                  <input id="acc-email" type="email" className={'lc-acc-input' + (errors.email?' lcf-acc-input--error':'')} value={form.email} onChange={e=>setForm(f=>({...f, email: e.target.value}))} placeholder="prenom@exemple.fr"/>
                  {errors.email && <div className="lcf-field-error"><WC_I.close s={12}/> {errors.email}</div>}
                  <div style={{ fontSize: 11, color: 'var(--gray-3)', marginTop: 6 }}>Ton code de vérification arrive par e-mail.</div>
                </div>
              )}
              {/* Téléphone : requis pour login ET inscription (le code SMS y est envoyé). */}
              {(mode === 'login' || mode === 'signup') && (
                <div className="lc-acc-field">
                  <label className="lc-acc-label" htmlFor="acc-phone">Téléphone</label>
                  <div className="lc-acc-phone">
                    <div className="lc-acc-phone-cc">🇫🇷 +33</div>
                    <input id="acc-phone" type="tel" className={'lc-acc-input lc-acc-phone-input' + (errors.phone?' lcf-acc-input--error':'')} value={form.phone} onChange={e=>setForm(f=>({...f, phone: e.target.value}))} placeholder="6 12 34 56 78"/>
                  </div>
                  {errors.phone && <div className="lcf-field-error"><WC_I.close s={12}/> {errors.phone}</div>}
                  {mode === 'login' && <div style={{ fontSize: 11, color: 'var(--gray-3)', marginTop: 6 }}>Ton téléphone = ta carte de fidélité (points liés au numéro).</div>}
                </div>
              )}
            </div>

            {mode === 'signup' && (
              <div className="lc-acc-trust">
                <div className="lc-acc-trust-row">
                  <span className="lc-acc-trust-icon"><WC_I.bolt s={14}/></span>
                  <span><b>Code e-mail</b> reçu en quelques secondes</span>
                </div>
                <div className="lc-acc-trust-row">
                  <span className="lc-acc-trust-icon"><WC_I.gift s={14}/></span>
                  <span><b>{earnLabel}</b> cumulé dès ta 1ʳᵉ commande</span>
                </div>
                <div className="lc-acc-trust-row">
                  <span className="lc-acc-trust-icon"><WC_I.shield s={14}/></span>
                  <span>Données <b>en France</b> · RGPD · pas revendues</span>
                </div>
              </div>
            )}

            <div className="lc-acc-foot">
              <button className="lc-acc-cta" onClick={submit} disabled={loading} style={{ opacity: loading?0.7:1 }}>
                {loading ? 'Chargement…' : (mode === 'login' ? 'Se connecter' : 'Recevoir le code')} <WC_I.arrow s={16} className="lc-acc-cta-arrow"/>
              </button>
              <div className="lc-acc-cgu">En continuant, tu acceptes nos <u>CGU</u> · <u>Confidentialité</u></div>
            </div>
          </div>
        </div>
      )}

      {mode === 'otp' && (
        <div className="lc-acc">
          <div className="lc-acc-art">
            <div className="lc-acc-art-bg"/>
            <div className="lc-acc-art-wordmark" style={{ top: '40%' }}>
              <div className="le" style={{ color: 'var(--ink)' }}>// EMAIL</div>
              <svg width="120" height="120" viewBox="0 0 120 120" style={{ margin: '20px auto 0' }}>
                <circle cx="60" cy="60" r="50" fill="none" stroke="#0A0A0A" strokeWidth="4"/>
                <path d="M30 40 L 90 40 L 80 60 L 90 80 L 30 80 Z" fill="#FF5A1F" stroke="#0A0A0A" strokeWidth="3"/>
                <circle cx="60" cy="60" r="6" fill="#0A0A0A"/>
              </svg>
            </div>
          </div>
          <div className="lc-acc-form">
            <button className="lc-acc-form-back" onClick={()=>setMode('signup')} aria-label="Retour à l'inscription"><WC_I.back s={16}/></button>
            <div className="lc-acc-eyebrow">// Étape 2 sur 2 · Code e-mail</div>
            <h1 className="lc-acc-title">Entre<br/>ton <span>code.</span></h1>
            <p className="lc-acc-sub">Code envoyé à <b style={{ color: 'var(--ink)' }}>{form.email.trim()}</b> · {/* [W3-HEAL-R2 P1-A] inline link orange on white = 3.11:1 FAIL → --orange-text 5.18:1 */}<span role="button" tabIndex={0} onClick={()=>setMode('signup')} onKeyDown={e=>{ if (e.key === 'Enter' || e.key === ' ') setMode('signup'); }} style={{ color: 'var(--orange-text)', fontWeight: 700, cursor: 'pointer' }}>Modifier</span></p>
            <div className="lc-otp-grid">
              {code.map((c, i) => (
                <input
                  key={i}
                  ref={refs[i]}
                  value={c}
                  className={'lc-otp-cell' + (c?' is-filled':'') + (otpError?' lcf-acc-input--error':'')}
                  onChange={e => onKey(i, e.target.value.slice(-1))}
                  onKeyDown={e => { if (e.key === 'Backspace' && !c && i > 0) refs[i-1].current?.focus(); }}
                  maxLength={1} inputMode="numeric"
                  aria-label={`Chiffre ${i+1}`}
                  aria-invalid={otpError}
                />
              ))}
            </div>
            {/* [W16 DEV-OTP 2026-07-20] Staging sans SMS : affiche le code renvoyé par le backend
                pour re-tester le web. Rendu SEULEMENT si ?dev (window.LC.isDev) ET dev_code présent
                (absent en prod = rien). Jamais une affordance client. */}
            {typeof window !== 'undefined' && window.LC && window.LC.isDev && devCode && (
              <div className="lc-otp-devhint" role="note" style={{ marginTop: 10, padding: '6px 10px', background: 'rgba(255,90,31,0.10)', border: '1px dashed var(--orange-text, #d8410a)', borderRadius: 8, fontSize: 13, color: 'var(--ink)', textAlign: 'center' }}>
                Code (dev) : <b style={{ letterSpacing: '3px' }}>{devCode}</b>
              </div>
            )}
            {/* [T1-08 HEAL 2026-06-05 WCAG 4.1.3] wrong-code error, live region */}
            {otpError && (
              <div className="lcf-field-error" role="alert" aria-live="assertive" style={{ marginTop: 10 }}>
                <WC_I.close s={12}/> Code incorrect. Réessaie.
              </div>
            )}
            <div className="lc-otp-resend">
              <span/>
              <button
                type="button"
                disabled={resendIn > 0}
                style={{ opacity: resendIn > 0 ? 0.5 : 1, cursor: resendIn > 0 ? 'default' : 'pointer' }}
                onClick={()=>{
                  if (resendIn > 0) return;
                  const ph = form.phone.replace(/\D/g, '');
                  // [HEAL 2026-07-19] AVANT : .catch(()=>{}) avalait l'échec/throttle puis setResendIn(29)
                  // relançait le compte à rebours comme si le SMS était parti. Désormais on ne réarme le
                  // compteur + ne vide les cases QUE sur succès ; en échec on affiche un avis et on laisse
                  // le bouton disponible (compteur NON réarmé) pour réessayer.
                  if (!api || ph.length < 6) { setErrors(er => ({ ...er, resend: 'Renvoi impossible, réessaie dans un instant.' })); return; }
                  setErrors(er => ({ ...er, resend: '' }));
                  api.guestOtp(ph)
                    .then((r)=>{ setDevCode(r && r.dev_code ? String(r.dev_code) : ''); setCode(['','','','']); setResendIn(29); refs[0].current && refs[0].current.focus(); })
                    .catch(()=>{ setErrors(er => ({ ...er, resend: 'Renvoi impossible, réessaie dans un instant.' })); });
                }}
              >
                {resendIn > 0 ? `Renvoyer (${resendIn}s)` : 'Renvoyer'}
              </button>
            </div>
            {/* [HEAL 2026-07-19] Avis d'échec/throttle du renvoi (n'était pas surfacé auparavant). */}
            {errors.resend && (
              <div className="lcf-field-error" role="alert" aria-live="assertive" style={{ marginTop: 8 }}>
                <WC_I.close s={12}/> {errors.resend}
              </div>
            )}
          </div>
        </div>
      )}

      {mode === 'success' && (
        <div style={{ padding: 60, textAlign: 'center', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', background: 'var(--yellow)', position: 'relative', overflow: 'hidden' }}>
          <div className="lcf-confetti">
            {Array.from({length: 24}).map((_,i) => {
              const colors = ['#FF5A1F', '#FFD93D', '#0A0A0A', '#1FA653'];
              return <i key={i} style={{ left: `${(i*4)%100}%`, background: colors[i%4], animationDelay: `${(i%10)*0.12}s` }}/>;
            })}
          </div>
          <div style={{ width: 100, height: 100, borderRadius: 999, background: 'var(--ink)', color: 'var(--green)', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '8px 8px 0 var(--orange)', zIndex: 2 }}>
            <WC_I.check s={56}/>
          </div>
          {/* [W3-HEAL-R2 P1-A] .lc-eyebrow=--orange-text on the yellow success bg = 3.76:1 FAIL for 11px → scope to ink (14.4:1) on this surface only; global rule unchanged (passes on light). */}
          <div className="lc-eyebrow" style={{ marginTop: 24, zIndex: 2, color: 'var(--ink)' }}>// Bienvenue au club</div>
          {/* [WEB-WIREUP 2026-06-26] Compte réel (token Sanctum) — plus de mention DÉMO. */}
          <h1 className="lc-display" style={{ fontSize: 'clamp(40px, 7vw, 84px)', margin: '12px 0 0', zIndex: 2 }}>Te <span style={{ color: 'var(--orange-text)' }}>voilà</span></h1>
          <h2 className="lc-display" style={{ fontSize: 'clamp(20px, 2.4vw, 28px)', margin: '6px 0 0', zIndex: 2 }}>Connecté</h2>
          <p style={{ marginTop: 16, color: 'var(--ink)', maxWidth: 400, fontSize: 15, lineHeight: 1.55, zIndex: 2 }}>Bienvenue chez Le Cayenne. Tes commandes sont envoyées directement à la caisse, et tes points fidélité se cumulent au retrait.</p>
          <button className="lc-btn lc-btn--ink" style={{ marginTop: 28, zIndex: 2 }} onClick={()=>{ onAuthed(); onClose(); }}>
            Commencer à commander <WC_I.arrow s={16}/>
          </button>
        </div>
      )}
    </WebModal>
  );
}

window.AccountFlow = AccountFlow;
