// web/components.jsx — shared components for Le Cayenne web

const { useState: wcS, useEffect: wcE, useRef: wcR } = React;

// Icons
const WC_I = {
  cart:    (p={}) => <svg width={p.s||16} height={p.s||16} viewBox="0 0 24 24" fill="none"><path d="M6 8h12l-1 12H7L6 8zM9 8V6a3 3 0 116 0v2" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  arrow:   (p={}) => <svg width={p.s||16} height={p.s||16} viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  back:    (p={}) => <svg width={p.s||16} height={p.s||16} viewBox="0 0 24 24" fill="none"><path d="M19 12H5M11 18l-6-6 6-6" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  close:   (p={}) => <svg width={p.s||18} height={p.s||18} viewBox="0 0 24 24" fill="none"><path d="M6 6l12 12M6 18L18 6" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"/></svg>,
  check:   (p={}) => <svg width={p.s||14} height={p.s||14} viewBox="0 0 24 24" fill="none"><path d="M5 12.5l4 4 10-10" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  heart:   (p={}) => <svg width={p.s||16} height={p.s||16} viewBox="0 0 24 24" fill={p.fill||'none'}><path d="M12 21s-7-4.5-9-9a5 5 0 019-3 5 5 0 019 3c-2 4.5-9 9-9 9z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  plus:    (p={}) => <svg width={p.s||16} height={p.s||16} viewBox="0 0 24 24" fill="none"><path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round"/></svg>,
  bolt:    (p={}) => <svg width={p.s||14} height={p.s||14} viewBox="0 0 24 24" fill="none"><path d="M13 3L5 14h6l-1 7 8-11h-6l1-7z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  shield:  (p={}) => <svg width={p.s||14} height={p.s||14} viewBox="0 0 24 24" fill="none"><path d="M12 3l8 3v6c0 4-3.5 7.5-8 9-4.5-1.5-8-5-8-9V6l8-3z" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round"/></svg>,
  gift:    (p={}) => <svg width={p.s||14} height={p.s||14} viewBox="0 0 24 24" fill="none"><rect x="3" y="9" width="18" height="11" rx="2" stroke="currentColor" strokeWidth="1.8"/><path d="M3 13h18M12 9v11M9 9c-1.5 0-2.5-1-2.5-2S7.5 5 9 5s3 1.5 3 4M15 9c1.5 0 2.5-1 2.5-2S16.5 5 15 5s-3 1.5-3 4" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/></svg>,
  trash:   (p={}) => <svg width={p.s||14} height={p.s||14} viewBox="0 0 24 24" fill="none"><path d="M4 7h16M9 7V5a2 2 0 012-2h2a2 2 0 012 2v2M7 7v12a2 2 0 002 2h6a2 2 0 002-2V7" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
};

// [GOAL-SYNC 2026-07-08] QR fidélité RÉEL — remplace l'ancien mock décoratif 13x13.
// Mint-on-display : POST /api/frontend/loyalty/qr via LC.api.loyaltyQr() (token signé
// « lqr.… », TTL 300 s, throttle backend 30/min — 1 mint / 5 min largement sous la limite).
// Rendu SVG par la lib vendorisée LOCALE window.qrcode (vendor/qrcode.js, chargée par
// index.html — AUCUN CDN) avec garde si absente → message FR « QR indisponible ».
// Compte à rebours ttl_seconds + re-mint AUTO à expiration + bouton « Actualiser ».
// ⚠ JAMAIS de format legacy « FK:<code> » (rejeté backend) ni de QR persistant offline.
function WebQR({ onCode }) {
  const [qr, setQr] = wcS({ token: '', ttl: 0, loading: false, error: '' });
  const aliveR = wcR(true);
  wcE(() => { aliveR.current = true; return () => { aliveR.current = false; }; }, []);

  const api = (typeof window !== 'undefined' && window.LC && window.LC.api) || null;
  const canMint = !!(api && typeof api.loyaltyQr === 'function' && typeof api.isAuthed === 'function' && api.isAuthed());

  const mint = () => {
    if (!canMint) { setQr({ token: '', ttl: 0, loading: false, error: 'QR indisponible' }); return; }
    setQr({ token: '', ttl: 0, loading: true, error: '' });
    api.loyaltyQr().then(d => {
      if (!aliveR.current) return;
      d = d || {};
      if (!d.token) { setQr({ token: '', ttl: 0, loading: false, error: 'QR indisponible — réessayez.' }); return; }
      setQr({ token: String(d.token), ttl: parseInt(d.ttl_seconds, 10) || 300, loading: false, error: '' });
      if (onCode && d.loyalty_code) onCode(String(d.loyalty_code));
    }).catch(e => {
      if (!aliveR.current) return;
      // Erreur réseau/HTTP GÉRÉE (jamais thrown) — message FR propre + bouton Actualiser.
      setQr({ token: '', ttl: 0, loading: false, error: (e && e.message) || 'QR indisponible — réessayez.' });
    });
  };

  // Mint-on-display (au montage uniquement).
  wcE(() => { mint(); }, []);

  // Compte à rebours 1 s.
  wcE(() => {
    if (!qr.token || qr.ttl <= 0) return;
    const t = setTimeout(() => { setQr(s => (s.token ? { ...s, ttl: s.ttl - 1 } : s)); }, 1000);
    return () => clearTimeout(t);
  }, [qr.token, qr.ttl]);
  // Expiré → re-mint automatique (uniquement depuis un token valide, jamais en boucle d'erreur).
  wcE(() => { if (qr.token && qr.ttl === 0) mint(); }, [qr.token, qr.ttl]);

  // Rendu SVG local (garde lib absente → fallback FR).
  let svgHtml = '';
  if (qr.token && typeof window !== 'undefined' && typeof window.qrcode === 'function') {
    try {
      const q = window.qrcode(0, 'M');
      q.addData(qr.token);
      q.make();
      svgHtml = q.createSvgTag({ cellSize: 3, margin: 0, scalable: true })
        .replace('<svg ', '<svg style="width:100%;height:100%;display:block" ');
    } catch (e) { svgHtml = ''; }
  }
  const mm = Math.floor(qr.ttl / 60), ss = ('0' + (qr.ttl % 60)).slice(-2);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, flexShrink: 0 }}>
      {svgHtml ? (
        <div
          className="lc-wallet-code-qr"
          role="img"
          aria-label="QR code fidélité — présentez-le en caisse"
          style={{ display: 'block', width: 92, height: 92, background: '#fff', padding: 4, borderRadius: 8, boxSizing: 'border-box' }}
          dangerouslySetInnerHTML={{ __html: svgHtml }}
        />
      ) : (
        <div className="lc-wallet-code-qr" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 92, height: 92, background: '#fff', border: '1px dashed var(--gray-2, #ccc)', borderRadius: 8, textAlign: 'center', fontSize: 10, fontWeight: 700, padding: 6, boxSizing: 'border-box', color: 'var(--gray-4, #666)' }}>
          {qr.loading ? 'Génération…' : 'QR indisponible'}
        </div>
      )}
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--gray-4, #666)', textAlign: 'center', maxWidth: 120 }} aria-live="polite">
        {svgHtml ? `Expire dans ${mm}:${ss}` : (qr.loading ? 'Génération du QR…' : (qr.error || 'QR indisponible'))}
      </div>
      {canMint && (
        <button
          type="button"
          onClick={mint}
          disabled={qr.loading}
          style={{ background: 'transparent', border: '1px solid var(--gray-2, #ccc)', borderRadius: 999, padding: '4px 10px', fontSize: 10, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', cursor: qr.loading ? 'default' : 'pointer', color: 'inherit' }}
        >Actualiser</button>
      )}
    </div>
  );
}

// Navbar
function WebNav({ route, onRoute, cartCount, onCart, isAuth, onAccount, onLogo }) {
  const [open, setOpen] = wcS(false);
  wcE(() => { setOpen(false); }, [route]);
  // [GOAL-SYNC-HEAL 2026-07-08] Fin du résidu démo (avatar + prénom codés en dur).
  // Avatar + libellé du bouton compte DÉRIVÉS du profil réel : initiale depuis
  // les derniers chiffres du téléphone (seule donnée client dispo via l'API),
  // sinon avatar générique neutre. Libellé sobre « Mon compte ».
  const navApi = (typeof window !== 'undefined' && window.LC && window.LC.api) || null;
  const acctPhone = (navApi && typeof navApi.getPhone === 'function' && navApi.getPhone()) || '';
  const acctAva = (String(acctPhone).replace(/\D/g, '').slice(-2)) || '👤';
  const acctLabel = 'Mon compte';
  const links = [
    { id: 'home',    label: 'Accueil' },
    { id: 'menu',    label: 'Menu' },
    { id: 'orders',  label: 'Commandes' },
    { id: 'loyalty', label: 'Fidélité' },
  ];
  return (
    <header className="lc-nav">
      <div className="lc-container lc-nav-row">
        <button onClick={onLogo} style={{ background: 'transparent', border: 0, padding: 0 }} className="lc-nav-brand">
          <span className="lc-nav-brand-mark">LC</span>
          <span>Le <b>Cayenne</b></span>
        </button>
        <nav className="lc-nav-links">
          {links.map(l => (
            <button key={l.id} onClick={() => onRoute(l.id)} className={'lc-nav-link' + (route===l.id?' is-on':'')}>{l.label}</button>
          ))}
        </nav>
        <div className="lc-nav-actions">
          <button onClick={onCart} className="lc-nav-btn-cart">
            <WC_I.cart/>
            <span>Panier</span>
            {/* [FULL-FLOW HEAL 2026-05-18 P0-3 WCAG SC 4.1.3] cart badge aria-live so screen readers announce updates */}
            {cartCount > 0 && <span className="lc-nav-btn-cart-dot" role="status" aria-live="polite" aria-label={`${cartCount} article${cartCount > 1 ? 's' : ''} dans le panier`}>{cartCount}</span>}
          </button>
          {isAuth ? (
            <button onClick={onAccount} className="lc-nav-btn-account">
              <span className="ava">{acctAva}</span>
              <span className="lc-hide-mobile">{acctLabel}</span>
            </button>
          ) : (
            <button onClick={onAccount} className="lc-nav-btn-account">Se connecter</button>
          )}
          {/* [Z-7 HEAL 2026-05-18 P2-2] aria-expanded + aria-controls reflect drawer state */}
          <button className="lc-nav-burger" onClick={()=>setOpen(o=>!o)} aria-label="Menu" aria-expanded={open} aria-controls="lc-mobile-menu">
            <span/><span/><span/>
          </button>
        </div>
      </div>
      {open && (
        <div id="lc-mobile-menu" className="lc-mobile-menu">
          {links.map(l => (
            <button key={l.id} onClick={() => { onRoute(l.id); setOpen(false); }} className={'lc-mobile-link' + (route===l.id?' is-on':'')}>
              {l.label}
              <em>→</em>
            </button>
          ))}
        </div>
      )}
    </header>
  );
}

// Footer
// [W.7.1 LCEN heal 2026-05-18] 4th column "Légal" added with anchor links to
// /legal/*.html static pages (LCEN art. 6 III + CGV L221-5 + RGPD + CNIL cookies + INCO 1169/2011).
function WebFooter({ onRoute }) {
  // [WEB-RT-06 heal 2026-06-04] Store buttons are prototype no-ops; surface a
  // "Démo V1" toast affordance instead of a silent dead click.
  const [storeToast, setStoreToast] = wcS('');
  wcE(() => {
    if (!storeToast) return;
    const t = setTimeout(() => setStoreToast(''), 2600);
    return () => clearTimeout(t);
  }, [storeToast]);
  const demoToast = (msg) => setStoreToast(msg);
  return (
    <footer className="lc-footer">
      <div className="lc-container">
        <div className="lc-footer-grid lc-footer-grid--4col">
          <div className="lc-footer-brand">
            <h3>Le <span>Cayenne</span></h3>
            <p>Sandwich Cayenne signature, tacos M & L, bols Frites/Riz, galettes — fait maison à Hénin-Beaumont. Du peuple, pour le peuple. À emporter sur place — et en livraison via Uber Eats.</p>
          </div>
          <div className="lc-footer-col">
            <h4>// Navigation</h4>
            <ul>
              <li><button onClick={()=>onRoute('home')}>Accueil</button></li>
              <li><button onClick={()=>onRoute('menu')}>Menu complet</button></li>
              <li><button onClick={()=>onRoute('loyalty')}>Programme fidélité</button></li>
            </ul>
          </div>
          <div className="lc-footer-col">
            <h4>// Contact</h4>
            <ul>
              <li><a href="tel:0365678291">03 65 67 82 91</a></li>
              <li><a href="mailto:contact@lecayenne.fr">contact@lecayenne.fr</a></li>
              <li><span>437 Rue Élie Gruyelle<br/>62110 Hénin-Beaumont</span></li>
              <li><span>Ouvert 18h — 00h</span></li>
            </ul>
          </div>
          <div className="lc-footer-col">
            <h4>// Légal</h4>
            <ul>
              <li><a href="legal/mentions.html">Mentions légales</a></li>
              <li><a href="legal/cgv.html">CGV</a></li>
              <li><a href="legal/privacy.html">Politique de confidentialité</a></li>
              <li><a href="legal/cookies.html">Politique de cookies</a></li>
              <li><a href="legal/allergens.html">Allergènes</a></li>
            </ul>
          </div>
        </div>
        <div className="lc-footer-end">
          <span>© 2026 LE CAYENNE · <a href="legal/cgv.html" style={{color:'inherit'}}>CGV</a> · <a href="legal/privacy.html" style={{color:'inherit'}}>CONFIDENTIALITÉ</a> · <a href="legal/mentions.html" style={{color:'inherit'}}>MENTIONS LÉGALES</a></span>
          <div className="lc-footer-stores">
            <button className="lc-footer-store" onClick={()=>demoToast("L'app iOS arrive bientôt — démo V1")}>📱 iOS</button>
            <button className="lc-footer-store" onClick={()=>demoToast("L'app Android arrive bientôt — démo V1")}>🤖 Android</button>
          </div>
        </div>
      </div>
      {storeToast && (
        <div className="lc-footer-toast" role="status" aria-live="polite">{storeToast}</div>
      )}
    </footer>
  );
}

// Modal shell
// [A11Y-MODAL-01 heal 2026-06-04 WCAG 2.1.2/2.4.3/4.1.2] role=dialog now always
// carries an accessible name (aria-labelledby > aria-label > FR fallback), traps
// Tab focus inside the dialog while open, and restores focus to the opener on close.
function WebModal({ open, onClose, children, full = false, label, labelledBy }) {
  const dialogRef = wcR(null);
  const openerRef = wcR(null);
  // Escape-to-close + focus trap (Tab cycling) while open.
  wcE(() => {
    if (!open) return;
    const getFocusable = () => {
      const node = dialogRef.current;
      if (!node) return [];
      return Array.prototype.slice.call(node.querySelectorAll(
        'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
      )).filter(el => el.offsetParent !== null || el === document.activeElement);
    };
    const onKey = (e) => {
      if (e.key === 'Escape') { onClose(); return; }
      if (e.key !== 'Tab') return;
      const node = dialogRef.current;
      if (!node) return;
      const items = getFocusable();
      if (items.length === 0) { e.preventDefault(); node.focus(); return; }
      const first = items[0], last = items[items.length - 1];
      const active = document.activeElement;
      if (e.shiftKey) {
        if (active === first || active === node || !node.contains(active)) { e.preventDefault(); last.focus(); }
      } else if (active === last) { e.preventDefault(); first.focus(); }
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open, onClose]);
  // Capture opener, move focus into the dialog on open, restore it on close.
  wcE(() => {
    if (!open) return;
    openerRef.current = document.activeElement;
    const node = dialogRef.current;
    if (node) {
      const focusable = node.querySelector(
        'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
      );
      (focusable || node).focus();
    }
    return () => {
      const opener = openerRef.current;
      if (opener && typeof opener.focus === 'function') opener.focus();
    };
  }, [open]);
  if (!open) return null;
  const nameProps = labelledBy ? { 'aria-labelledby': labelledBy } : { 'aria-label': label || 'Boîte de dialogue' };
  return (
    <div className="lc-modal-backdrop" onClick={onClose}>
      {/* [MOBILE-FULLSCREEN 2026-07-12] La taille du modal "full" est pilotée par la CSS
          (.lc-modal--full) et NON par un style inline : un style inline battrait les media
          queries et empêcherait le plein écran mobile (footer détaché → « page pas complète »). */}
      <div ref={dialogRef} tabIndex={-1} className={"lc-modal" + (full ? " lc-modal--full" : "")} role="dialog" aria-modal="true" {...nameProps} onClick={e=>e.stopPropagation()}>
        <button onClick={onClose} className="lc-modal-close" aria-label="Fermer"><WC_I.close/></button>
        {children}
      </div>
    </div>
  );
}

Object.assign(window, { WC_I, WebNav, WebFooter, WebModal, WebQR });
