// =============================================================================
// APP — composant racine de l'application + montage React
// =============================================================================

function App() {
  // ===== AUTH + ROUTING =====
  const [authReady, setAuthReady] = useState(!fbAuth);
  const [firebaseUser, setFirebaseUser] = useState(null);
  // route : 'loading' | 'landing' | 'ownerAuth' | 'eventPicker' | 'eventCreate' | 'barmanLogin' | 'app' | 'eventNotFound'
  const [route, setRoute] = useState('loading');
  const [eventId, setEventId] = useState(null);
  const [eventMeta, setEventMeta] = useState(null);
  const [member, setMember] = useState(null); // { name, role }
  const [userEvents, setUserEvents] = useState([]);
  const [creatingEvent, setCreatingEvent] = useState(false);
  const [authError, setAuthError] = useState('');
  const [confirm, setConfirm] = useState(null); // { title, body, confirmLabel, confirmVariant, onConfirm }

  // Alias rétro-compatible : tout le code de rendu utilise `user`. On le synchronise avec `member`.
  const user = member;
  const setUser = setMember;

  // ===== DATA STATES (relevant once we have eventId + member) =====
  const [view, setView] = useState('sales');
  const [drinks, setDrinks] = useState(DEFAULT_DRINKS);
  const [sales, setSales] = useState([]);
  const [cart, setCart] = useState({});
  const [sessionId] = useState(() => genId());
  const [myKeys, setMyKeys] = useState([]);
  const [cartOpen, setCartOpen] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const [toast, setToast] = useState(null);
  const [syncStatus, setSyncStatus] = useState('connecting');
  const [editingDrink, setEditingDrink] = useState(null);
  const [confirmReset, setConfirmReset] = useState(false);
  const [confirmDrinksReset, setConfirmDrinksReset] = useState(false);
  const [flashTotal, setFlashTotal] = useState(false);
  const [activeCategory, setActiveCategory] = useState('all');
  const [photos, setPhotos] = useState({});
  const [archives, setArchives] = useState([]);
  const [tabs, setTabs] = useState([]);
  const [activeTabId, setActiveTabId] = useState(null);
  const [tabsScreenOpen, setTabsScreenOpen] = useState(false);
  const [pendingSales, setPendingSales] = useState([]);
  const [quickMode, setQuickMode] = useState(false);
  const [discount, setDiscount] = useState(0);
  const [lastBackup, setLastBackup] = useState(null);
  const isOnline = syncStatus === 'online';

  // Hydrate per-event localStorage states quand eventId devient connu (= changement d'événement actif)
  useEffect(() => {
    if (!eventId) return;
    // Drinks cache (boot offline)
    try {
      const cached = localStorage.getItem(eventStorageKey(eventId, 'drinks-cache'));
      if (cached) {
        const parsed = JSON.parse(cached);
        if (Array.isArray(parsed) && parsed.length > 0) setDrinks(parsed);
      }
    } catch {}
    // File d'attente offline
    try {
      const raw = localStorage.getItem(eventStorageKey(eventId, 'pending-sales')) || '[]';
      setPendingSales(JSON.parse(raw));
    } catch { setPendingSales([]); }
    // Mode rapide
    try { setQuickMode(localStorage.getItem(eventStorageKey(eventId, 'quickmode')) === '1'); }
    catch { setQuickMode(false); }
    // Backup
    try {
      const raw = localStorage.getItem(eventStorageKey(eventId, 'backup'));
      const parsed = raw ? JSON.parse(raw) : null;
      setLastBackup(parsed?.timestamp || null);
    } catch { setLastBackup(null); }
    // Réinitialise les états volatils (panier, vues actives) au changement d'événement
    setCart({}); setMyKeys([]); setCartOpen(false); setActiveTabId(null);
    setTabsScreenOpen(false); setView('sales');
  }, [eventId]);

  const toggleQuickMode = () => {
    setQuickMode(prev => {
      const next = !prev;
      try { localStorage.setItem(eventStorageKey(eventId, 'quickmode'), next ? '1' : '0'); } catch {}
      if (next) { setCart({}); setCartOpen(false); setDiscount(0); }
      return next;
    });
  };

  const totalRevenue = useMemo(() => sales.reduce((s, x) => s + (x.total || 0), 0), [sales]);
  const cartItems = useMemo(() => Object.entries(cart).map(([id, qty]) => {
    const d = drinks.find(d => d.id === id);
    return d ? { ...d, qty } : null;
  }).filter(Boolean), [cart, drinks]);
  const cartSubtotal = useMemo(() => cartItems.reduce((s, i) => s + i.price * i.qty, 0), [cartItems]);
  // cartTotal = somme du panier après réduction éventuelle
  const cartTotal = useMemo(() => cartSubtotal * (1 - (discount || 0)), [cartSubtotal, discount]);
  const cartCount = useMemo(() => cartItems.reduce((s, i) => s + i.qty, 0), [cartItems]);

  // Compteur d'items dont le stock effectif est ≤ LOW_STOCK_THRESHOLD (pour badge sales view)
  const lowStockCount = useMemo(() => {
    return drinks.filter(d => {
      if (d.stockOnly) return false;
      const eff = getEffectiveStock(d, drinks);
      return eff !== null && eff <= LOW_STOCK_THRESHOLD;
    }).length;
  }, [drinks]);

  // Cache local des drinks pour pouvoir booter hors-ligne (Lot 3)
  useEffect(() => {
    if (!eventId) return;
    if (drinks && drinks.length > 0) {
      try { localStorage.setItem(eventStorageKey(eventId, 'drinks-cache'), JSON.stringify(drinks)); }
      catch (e) { /* quota dépassé : on ignore, c'est juste du cache */ }
    }
  }, [drinks, eventId]);

  // Persistance de la file d'attente offline
  useEffect(() => {
    if (!eventId) return;
    try { localStorage.setItem(eventStorageKey(eventId, 'pending-sales'), JSON.stringify(pendingSales)); }
    catch {}
  }, [pendingSales, eventId]);

  // Décrément optimiste du stock pour les ventes en attente (UX cohérente offline)
  const pendingStockDeltas = useMemo(() => {
    const map = {};
    pendingSales.forEach(({ sale }) => {
      (sale.items || []).forEach(item => {
        const drink = drinks.find(d => d.id === item.drinkId);
        const targets = getStockTargets(drink, drinks);
        targets.forEach(target => {
          map[target.drinkId] = (map[target.drinkId] || 0) - (target.decrementPerUnit * (item.quantity || 0));
        });
      });
    });
    return map;
  }, [pendingSales, drinks]);

  // drinks "affichés" : avec décrément optimiste appliqué. À utiliser dans toutes les vues.
  // Les opérations d'écriture utilisent `drinks` (vérité Firebase).
  const displayDrinks = useMemo(() =>
    drinks.map(d => ({
      ...d,
      stock: (d.stock || 0) + (pendingStockDeltas[d.id] || 0),
    })), [drinks, pendingStockDeltas]);

  const showToast = (message, type = 'success') => {
    setToast({ message, type, id: Date.now() });
    setTimeout(() => setToast(null), 2000);
  };

  useEffect(() => {
    if (firebaseError || !db) { setIsLoading(false); setSyncStatus('offline'); return; }
    // Connexion Firebase à surveiller (toujours, indépendant de l'événement)
    const connRef = db.ref('.info/connected');
    const onConn = (snap) => setSyncStatus(snap.val() ? 'online' : 'offline');
    connRef.on('value', onConn);
    return () => connRef.off('value', onConn);
  }, []);

  // Écoute l'état d'auth Firebase
  useEffect(() => {
    if (!fbAuth) return;
    const unsub = fbAuth.onAuthStateChanged(u => {
      setFirebaseUser(u ? {
        uid: u.uid, email: u.email, displayName: u.displayName,
        isAnonymous: u.isAnonymous,
      } : null);
      setAuthReady(true);
    });
    return () => unsub();
  }, []);

  // Décide la route initiale dès que l'auth est résolue
  useEffect(() => {
    if (!authReady || firebaseError) return;
    // Si on est déjà dans 'app' ou 'eventCreate' ou 'ownerAuth', on ne re-route pas (l'utilisateur est en cours d'action)
    if (route === 'app' || route === 'eventCreate' || route === 'ownerAuth') return;

    const urlEventId = getEventIdFromURL();
    const savedEventId = getCurrentEventId();
    const targetEventId = urlEventId || savedEventId;

    if (!firebaseUser) {
      // Non authentifié
      if (urlEventId) {
        // Lien partagé : on tente une connexion anonyme
        if (fbAuth) {
          fbAuth.signInAnonymously().catch(err => {
            console.error('anon signin', err);
            setRoute('eventNotFound');
          });
          // L'écouteur d'auth va re-déclencher cet effet quand firebaseUser sera set
        }
        return;
      }
      setRoute('landing');
      return;
    }

    if (firebaseUser.isAnonymous) {
      // Compte anonyme : doit avoir un eventId (sinon flow incohérent → on déconnecte)
      if (!targetEventId) { fbAuth.signOut(); return; }
      if (eventId !== targetEventId) setEventId(targetEventId);
      return;
    }

    // Compte réel : si on a un eventId cible, on l'utilise. Sinon → picker.
    if (targetEventId) {
      if (eventId !== targetEventId) setEventId(targetEventId);
    } else {
      setRoute('eventPicker');
    }
  }, [authReady, firebaseUser, route, eventId]);

  // Une fois l'auth Firebase confirmée pendant que l'utilisateur est sur l'écran de
  // connexion (Google ou email/password), on bascule automatiquement vers le picker.
  useEffect(() => {
    if (route === 'ownerAuth' && firebaseUser && !firebaseUser.isAnonymous) {
      setRoute('eventPicker');
    }
  }, [route, firebaseUser]);

  // Charge la meta de l'événement courant. Le listener reste actif même quand on est
  // dans l'app, pour que les modifs (nom, membres) côté Firebase se reflètent dans l'UI.
  useEffect(() => {
    if (!eventId || !db) return;
    const metaRef = db.ref(`events/${eventId}/meta`);
    const handler = metaRef.on('value', snap => {
      const meta = snap.val();
      if (!meta) {
        setEventMeta(null);
        if (route !== 'app') setRoute('eventNotFound');
        return;
      }
      const members = Array.isArray(meta.members) ? meta.members : (meta.members ? Object.values(meta.members) : []);
      const normalized = { ...meta, members };
      setEventMeta(normalized);
      // Le routing automatique ne se fait qu'avant l'entrée dans l'app
      if (route === 'app') return;
      const cached = getEventMember(eventId);
      if (cached) {
        const stillExists = members.some(m => m.name === cached.name && m.role === cached.role);
        if (stillExists) {
          setMember(cached);
          setRoute('app');
          setCurrentEventId(eventId);
          return;
        }
        clearEventMember(eventId);
      }
      setRoute('barmanLogin');
    });
    return () => metaRef.off('value', handler);
  }, [eventId, route]);

  // Charge la liste des événements de l'utilisateur quand il est sur le picker
  useEffect(() => {
    if (route !== 'eventPicker' || !firebaseUser?.uid || !db) return;
    const evRef = db.ref(`users/${firebaseUser.uid}/events`);
    let cancelled = false;
    const handler = evRef.on('value', async snap => {
      if (cancelled) return;
      const v = snap.val() || {};
      const ids = Object.keys(v);
      const events = [];
      for (const id of ids) {
        try {
          const metaSnap = await db.ref(`events/${id}/meta`).once('value');
          const meta = metaSnap.val();
          if (meta) events.push({ ...meta, _key: id });
        } catch {}
      }
      events.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
      if (!cancelled) setUserEvents(events);
    });
    return () => { cancelled = true; evRef.off('value', handler); };
  }, [route, firebaseUser]);

  // === ACTIONS AUTH / EVENT ===

  const handleOwnerSignout = async () => {
    try { await fbAuth.signOut(); } catch {}
    setFirebaseUser(null);
    setMember(null); setEventId(null); setEventMeta(null);
    setCurrentEventId(null);
    setRoute('landing');
  };

  const handleMemberLogin = (memberObj) => {
    setMember(memberObj);
    setEventMember(eventId, memberObj);
    setCurrentEventId(eventId);
    setRoute('app');
  };

  const handleMemberLogout = () => {
    if (eventId) clearEventMember(eventId);
    setMember(null);
    setRoute('barmanLogin');
  };

  const handleSwitchEvent = () => {
    // Quitte l'événement actuel sans toucher à l'auth Firebase (utile pour un owner)
    if (eventId) clearEventMember(eventId);
    setMember(null);
    setEventId(null);
    setEventMeta(null);
    setCurrentEventId(null);
    setRoute(firebaseUser?.isAnonymous ? 'landing' : 'eventPicker');
    // Nettoie l'URL pour éviter de re-router vers le même event
    try { window.history.replaceState({}, '', window.location.pathname); } catch {}
  };

  const handleCreateEvent = async ({ name, members: memberInputs }) => {
    if (!firebaseUser?.uid || !db) return;
    setCreatingEvent(true);
    try {
      const newEventId = genEventId();
      // Hash des mots de passe (avec sel par membre = nom + eventId)
      const hashedMembers = await Promise.all(memberInputs.map(async m => {
        const salt = newEventId + ':' + m.name;
        const passwordHash = await hashPassword(m.password, salt);
        return { name: m.name, role: m.role, salt, passwordHash };
      }));
      const meta = {
        name,
        ownerUid: firebaseUser.uid,
        createdAt: Date.now(),
        members: hashedMembers,
      };
      // 1. Crée l'événement (meta + drinks par défaut)
      await db.ref(`events/${newEventId}`).set({
        meta,
        drinks: DEFAULT_DRINKS,
      });
      // 2. Indexe l'événement chez le user
      await db.ref(`users/${firebaseUser.uid}/events/${newEventId}`).set(true);
      // 3. Bascule vers cet événement (route va ré-évaluer)
      setEventId(newEventId);
      setCurrentEventId(newEventId);
      setRoute('loading'); // sera remplacé par 'barmanLogin' une fois la meta chargée
    } catch (e) {
      console.error('event create failed', e);
      setAuthError('Création échouée. Réessaie.');
      setCreatingEvent(false);
      return;
    }
    setCreatingEvent(false);
  };

  const updateEventMembers = async (newMembers) => {
    if (!eventId || !db) throw new Error('Pas de connexion');
    const existing = (eventMeta?.members || []);
    const hashed = await Promise.all(newMembers.map(async m => {
      if (m.password) {
        const salt = eventId + ':' + m.name;
        const passwordHash = await hashPassword(m.password, salt);
        return { name: m.name, role: m.role, salt, passwordHash };
      }
      const prev = existing.find(x => x.name === m.name);
      if (prev?.passwordHash) {
        return { name: m.name, role: m.role, salt: prev.salt, passwordHash: prev.passwordHash };
      }
      throw new Error(`Mot de passe manquant pour "${m.name}"`);
    }));
    await db.ref(`events/${eventId}/meta/members`).set(hashed);
    return true;
  };

  const updateEventName = async (newName) => {
    if (!eventId || !db || !newName) return false;
    try { await db.ref(`events/${eventId}/meta/name`).set(newName); return true; }
    catch { return false; }
  };

  const deleteEvent = async () => {
    if (!eventId || !db || !firebaseUser?.uid) return false;
    try {
      await db.ref(`events/${eventId}`).remove();
      await db.ref(`users/${firebaseUser.uid}/events/${eventId}`).remove();
      // Nettoie le localStorage de cet event
      try {
        ['drinks-cache', 'pending-sales', 'quickmode', 'backup', 'member'].forEach(k => {
          localStorage.removeItem(eventStorageKey(eventId, k));
        });
      } catch {}
      setMember(null); setEventId(null); setEventMeta(null);
      setCurrentEventId(null); setRoute('eventPicker');
      try { window.history.replaceState({}, '', window.location.pathname); } catch {}
      return true;
    } catch (e) { console.error(e); return false; }
  };

  // Subscriptions data scopées par événement (drinks, sales, photos, archives, tabs)
  // Re-créées à chaque changement d'eventId.
  useEffect(() => {
    if (firebaseError || !db) return;
    if (!eventId || route !== 'app') return;
    setIsLoading(true);
    const drinksRef = db.ref(`events/${eventId}/drinks`);
    const onDrinks = (snap) => {
      const v = snap.val();
      if (v && Array.isArray(v) && v.length > 0) {
        let migrated = v.slice();
        let needsWrite = false;

        // Migration 1 : la catégorie 'eau' a été fusionnée dans 'soft'
        migrated = migrated.map(d => {
          if (d && d.category === 'eau') { needsWrite = true; return { ...d, category: 'soft' }; }
          return d;
        });

        // Migration 2 : ajouter les ingrédients de cocktails (si absents)
        const COCKTAIL_INGREDIENTS = {
          denise:   [{ parentId: 'aperol', amount: 0.04 }],
          huguette: [{ parentId: 'sureau', amount: 0.04 }],
          germaine: [{ parentId: 'peche',  amount: 0.04 }],
        };
        migrated = migrated.map(d => {
          if (d && COCKTAIL_INGREDIENTS[d.id] && !d.ingredients) {
            needsWrite = true;
            return { ...d, ingredients: COCKTAIL_INGREDIENTS[d.id] };
          }
          return d;
        });

        // Migration 3 : ajouter les items de stock liqueurs (si absents)
        const NEW_STOCK_ITEMS = [
          { id: 'aperol', name: 'Apérol',            price: 0, icon: 'Bottle', category: 'cocktail', stockEnabled: true, stock: 0, stockUnit: 'L', stockOnly: true },
          { id: 'sureau', name: 'Liqueur de Sureau', price: 0, icon: 'Bottle', category: 'cocktail', stockEnabled: true, stock: 0, stockUnit: 'L', stockOnly: true },
          { id: 'peche',  name: 'Liqueur de pêche',  price: 0, icon: 'Bottle', category: 'cocktail', stockEnabled: true, stock: 0, stockUnit: 'L', stockOnly: true },
        ];
        for (const item of NEW_STOCK_ITEMS) {
          if (!migrated.some(d => d && d.id === item.id)) {
            migrated.push(item);
            needsWrite = true;
          }
        }

        // Migration 4 : conversion cl → L pour les ingrédients de cocktail
        const INGREDIENT_IDS = ['aperol', 'sureau', 'peche'];
        migrated = migrated.map(d => {
          if (d && INGREDIENT_IDS.includes(d.id) && d.stockUnit === 'cl') {
            needsWrite = true;
            return { ...d, stockUnit: 'L', stock: (d.stock || 0) / 100 };
          }
          return d;
        });
        const COCKTAIL_IDS = Object.keys(COCKTAIL_INGREDIENTS);
        migrated = migrated.map(d => {
          if (d && COCKTAIL_IDS.includes(d.id) && Array.isArray(d.ingredients)) {
            const fixed = d.ingredients.map(ing => {
              if (INGREDIENT_IDS.includes(ing.parentId) && ing.amount >= 1) {
                return { ...ing, amount: ing.amount / 100 };
              }
              return ing;
            });
            const changed = fixed.some((ing, i) => ing.amount !== d.ingredients[i].amount);
            if (changed) { needsWrite = true; return { ...d, ingredients: fixed }; }
          }
          return d;
        });

        // Migration 5 : ajouter les items "nourriture" (si absents)
        const NEW_FOOD_ITEMS = [
          { id: 'indien', name: 'Nourriture indienne', price: 8.00, icon: 'Soup',     category: 'snack', stockEnabled: false, stock: 0 },
          { id: 'burger', name: 'Burger',              price: 8.00, icon: 'Sandwich', category: 'snack', stockEnabled: false, stock: 0 },
        ];
        for (const item of NEW_FOOD_ITEMS) {
          if (!migrated.some(d => d && d.id === item.id)) {
            migrated.push(item);
            needsWrite = true;
          }
        }

        if (needsWrite) {
          drinksRef.set(migrated).catch(e => console.error('migration error', e));
        }
        setDrinks(migrated);
      }
      else drinksRef.set(DEFAULT_DRINKS).catch(e => console.error('init drinks', e));
      setIsLoading(false);
    };
    drinksRef.on('value', onDrinks);
    const photosRef = db.ref(`events/${eventId}/photos`);
    const onPhotos = (snap) => { setPhotos(snap.val() || {}); };
    photosRef.on('value', onPhotos);
    const salesRef = db.ref(`events/${eventId}/sales`);
    const onSales = (snap) => {
      const v = snap.val() || {};
      const arr = Object.entries(v).map(([key, sale]) => ({ ...sale, _key: key }));
      arr.sort((a, b) => a.timestamp - b.timestamp);
      setSales(arr);
    };
    salesRef.on('value', onSales);
    const archivesRef = db.ref(`events/${eventId}/archives`);
    const onArchives = (snap) => {
      const v = snap.val() || {};
      const arr = Object.entries(v).map(([key, archive]) => ({ ...archive, _key: key }));
      arr.sort((a, b) => (b.closedAt || 0) - (a.closedAt || 0));
      setArchives(arr);
    };
    archivesRef.on('value', onArchives);
    const tabsRef = db.ref(`events/${eventId}/tabs`);
    const onTabs = (snap) => {
      const v = snap.val() || {};
      const arr = Object.entries(v).map(([key, tab]) => ({ ...tab, _key: key }));
      arr.sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0));
      setTabs(arr);
    };
    tabsRef.on('value', onTabs);
    return () => { drinksRef.off('value', onDrinks); photosRef.off('value', onPhotos); salesRef.off('value', onSales); archivesRef.off('value', onArchives); tabsRef.off('value', onTabs); };
  }, [eventId, route]);

  const setDrinkPhoto = async (drinkId, dataUrl) => {
    if (!db) return;
    try {
      if (dataUrl) await db.ref(`events/${eventId}/photos/${drinkId}`).set(dataUrl);
      else await db.ref(`events/${eventId}/photos/${drinkId}`).remove();
    } catch (e) { showToast('Erreur photo', 'error'); }
  };

  const addToCart = (id) => setCart(p => ({ ...p, [id]: (p[id] || 0) + 1 }));
  const decrementCart = (id) => setCart(p => {
    const c = p[id] || 0;
    if (c <= 1) { const { [id]: _, ...r } = p; return r; }
    return { ...p, [id]: c - 1 };
  });
  const clearCart = () => { setCart({}); setCartOpen(false); setDiscount(0); };

  // Construit le payload sale (utilisé par _processSale et le sync offline).
  const _buildSale = (cartList, paymentMethodId, discountValue) => {
    const isOffered = paymentMethodId === 'offert';
    const subtotal = cartList.reduce((s, i) => s + i.price * i.qty, 0);
    const effectiveDiscount = isOffered ? 0 : (discountValue || 0);
    const total = isOffered ? 0 : subtotal * (1 - effectiveDiscount);
    const items = cartList.map(({ id, name, price, qty }) => ({ drinkId: id, name, price, quantity: qty }));
    return {
      items, total, subtotal, discount: effectiveDiscount,
      paymentMethod: paymentMethodId, timestamp: Date.now(),
      sessionId, userName: user.name,
    };
  };

  // Cœur de la vente : effectue le push Firebase, décrémente le stock, déclenche les alertes.
  // En mode hors-ligne (Lot 3), la vente est mise dans une file d'attente locale et synchronisée plus tard.
  const _processSale = async (cartList, paymentMethodId, discountValue) => {
    if (cartList.length === 0) return false;
    const sale = _buildSale(cartList, paymentMethodId, discountValue);

    // Cas hors-ligne : on met en file d'attente immédiatement, sans toucher Firebase
    if (!isOnline || !db) {
      const localId = `local_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
      setPendingSales(prev => [...prev, { localId, sale, attempts: 0 }]);
      setMyKeys(prev => [...prev, localId]); // pour permettre l'undo même offline
      return true;
    }

    // Cas en ligne : push direct
    try {
      const ref = db.ref(`events/${eventId}/sales`).push();
      await ref.set(sale);
      // Décrément du stock + détection des items qui passent en alerte
      const projectedStock = {};
      for (const ci of cartList) {
        const targets = getStockTargets(ci, drinks);
        for (const target of targets) {
          const idx = drinks.findIndex(d => d.id === target.drinkId);
          if (idx < 0) continue;
          const dec = target.decrementPerUnit * ci.qty;
          if (!(target.drinkId in projectedStock)) {
            projectedStock[target.drinkId] = drinks[idx]?.stock || 0;
          }
          projectedStock[target.drinkId] -= dec;
          try {
            await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) - dec) * 100) / 100));
          } catch (e) { console.error('stock decrement failed', e); }
        }
      }
      for (const [drinkId, newStock] of Object.entries(projectedStock)) {
        const drink = drinks.find(d => d.id === drinkId);
        if (!drink || !drink.stockEnabled) continue;
        const prev = drink.stock || 0;
        const unit = drink.stockUnit ? ` ${drink.stockUnit}` : '';
        if (prev > 0 && newStock <= 0) showToast(`⚠️ ${drink.name} épuisé !`, 'error');
        else if (prev > LOW_STOCK_THRESHOLD && newStock <= LOW_STOCK_THRESHOLD && newStock > 0) {
          showToast(`Stock bas : ${drink.name} (${Math.floor(newStock)}${unit})`, 'info');
        }
      }
      setMyKeys(prev => [...prev, ref.key]);
      return true;
    } catch (e) {
      // Erreur réseau : on bascule en file d'attente plutôt que perdre la vente
      console.warn('sale push failed, queueing locally', e);
      const localId = `local_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
      setPendingSales(prev => [...prev, { localId, sale, attempts: 1, lastError: e?.message || 'erreur' }]);
      setMyKeys(prev => [...prev, localId]);
      showToast('Vente mise en attente', 'info');
      return true;
    }
  };

  // Synchronise la file d'attente offline vers Firebase. Appelé sur reconnexion + périodiquement.
  // Utilise une ref de garde pour ne pas lancer plusieurs syncs en parallèle.
  const syncingRef = useRef(false);
  const syncPendingSales = async () => {
    if (syncingRef.current || !isOnline || !db || pendingSales.length === 0) return;
    syncingRef.current = true;
    let synced = 0;
    try {
      // Capture de la liste à un instant T (les ajouts pendant le sync seront pris au prochain tour)
      const queue = pendingSales.slice();
      for (const item of queue) {
        try {
          const ref = db.ref(`events/${eventId}/sales`).push();
          await ref.set(item.sale);
          // Décrément du stock pour cette vente. Pas d'alertes ici (déjà tardives).
          for (const ci of (item.sale.items || [])) {
            const drinkRef = drinks.find(d => d.id === ci.drinkId);
            const targets = getStockTargets(drinkRef, drinks);
            for (const target of targets) {
              const idx = drinks.findIndex(d => d.id === target.drinkId);
              if (idx < 0) continue;
              const dec = target.decrementPerUnit * (ci.quantity || 0);
              try {
                await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) - dec) * 100) / 100));
              } catch (e) { console.error('sync stock decrement failed', e); }
            }
          }
          // Retirer de la file + remplacer le localId par la vraie clé Firebase dans myKeys
          setPendingSales(prev => prev.filter(p => p.localId !== item.localId));
          setMyKeys(prev => prev.map(k => k === item.localId ? ref.key : k));
          synced++;
        } catch (e) {
          console.error('sync item failed', e);
          setPendingSales(prev => prev.map(p =>
            p.localId === item.localId ? { ...p, attempts: (p.attempts || 0) + 1, lastError: e?.message || 'erreur' } : p
          ));
          break; // On arrête au premier échec pour ne pas marteler
        }
      }
    } finally {
      syncingRef.current = false;
    }
    if (synced > 0) showToast(`${synced} vente${synced > 1 ? 's' : ''} synchronisée${synced > 1 ? 's' : ''}`, 'info');
  };

  // Trigger sync : à la reconnexion + toutes les SYNC_RETRY_INTERVAL_MS
  useEffect(() => {
    if (!isOnline) return;
    syncPendingSales();
    const id = setInterval(() => { if (isOnline) syncPendingSales(); }, SYNC_RETRY_INTERVAL_MS);
    return () => clearInterval(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isOnline]);
  // Re-tente aussi quand de nouveaux items sont ajoutés à la file (au cas où on rate une fenêtre)
  useEffect(() => {
    if (isOnline && pendingSales.length > 0) {
      const t = setTimeout(syncPendingSales, 500);
      return () => clearTimeout(t);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [pendingSales.length, isOnline]);

  const completeSale = async (paymentMethodId) => {
    const ok = await _processSale(cartItems, paymentMethodId, discount);
    if (!ok) return;
    setCart({}); setCartOpen(false); setDiscount(0);
    setFlashTotal(true);
    setTimeout(() => setFlashTotal(false), 600);
    const isOffered = paymentMethodId === 'offert';
    const total = isOffered ? 0 : cartSubtotal * (1 - (discount || 0));
    if (!isOnline) showToast(`En attente · ${formatPrice(total)}`, 'info');
    else showToast(isOffered ? `Offert : ${formatPrice(cartSubtotal)}` : `Vente : ${formatPrice(total)}`);
  };

  // Mode tap-rapide : un appui sur une carte = une vente cash de 1 unité, sans panier.
  const quickSale = async (drinkId) => {
    const d = drinks.find(x => x.id === drinkId);
    if (!d) return;
    const ok = await _processSale([{ ...d, qty: 1 }], 'cash', 0);
    if (!ok) return;
    setFlashTotal(true);
    setTimeout(() => setFlashTotal(false), 400);
    showToast(`⚡ ${d.name} · ${formatPrice(d.price)}`);
  };

  // Annule une vente arbitraire : restaure le stock + supprime de Firebase.
  // Si la clé est locale (vente offline en attente), on la retire juste de la file.
  const cancelSale = async (saleKey) => {
    if (!saleKey) return false;
    if (saleKey.startsWith('local_')) {
      // Vente en attente : pas encore poussée à Firebase, donc rien à restaurer côté stock.
      setPendingSales(prev => prev.filter(p => p.localId !== saleKey));
      setMyKeys(prev => prev.filter(k => k !== saleKey));
      showToast('Vente annulée', 'info');
      return true;
    }
    if (!db) return false;
    try {
      const snap = await db.ref(`events/${eventId}/sales/${saleKey}`).once('value');
      const sale = snap.val();
      if (!sale) { showToast('Vente introuvable', 'error'); return false; }
      if (Array.isArray(sale.items)) {
        for (const it of sale.items) {
          const drink = drinks.find(d => d.id === it.drinkId);
          const targets = getStockTargets(drink, drinks);
          for (const target of targets) {
            const idx = drinks.findIndex(d => d.id === target.drinkId);
            if (idx < 0) continue;
            const inc = target.decrementPerUnit * (it.quantity || 0);
            try {
              await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) + inc) * 100) / 100));
            } catch (e) { console.error('stock restore failed', e); }
          }
        }
      }
      await db.ref(`events/${eventId}/sales/${saleKey}`).remove();
      setMyKeys(prev => prev.filter(k => k !== saleKey));
      showToast('Vente annulée', 'info');
      return true;
    } catch (e) { showToast('Erreur', 'error'); return false; }
  };

  const undoLastSale = async () => {
    if (myKeys.length === 0) return;
    const lastKey = myKeys[myKeys.length - 1];
    await cancelSale(lastKey);
  };

  // ===== ARDOISES (Lot 3) — exigent une connexion en ligne =====
  // Quand un item est ajouté à une ardoise, le stock est décrémenté immédiatement
  // (l'item est servi et consommé, indépendamment du paiement).
  // À la clôture, on ne re-décrémente pas — on crée juste l'enregistrement de vente.

  const _alertLowStock = (projectedStock) => {
    for (const [drinkId, newStock] of Object.entries(projectedStock)) {
      const drink = drinks.find(d => d.id === drinkId);
      if (!drink || !drink.stockEnabled) continue;
      const prev = drink.stock || 0;
      const unit = drink.stockUnit ? ` ${drink.stockUnit}` : '';
      if (prev > 0 && newStock <= 0) showToast(`⚠️ ${drink.name} épuisé !`, 'error');
      else if (prev > LOW_STOCK_THRESHOLD && newStock <= LOW_STOCK_THRESHOLD && newStock > 0) {
        showToast(`Stock bas : ${drink.name} (${Math.floor(newStock)}${unit})`, 'info');
      }
    }
  };

  const createTab = async (name) => {
    if (!isOnline || !db || !user) { showToast('Hors ligne — création impossible', 'error'); return null; }
    const trimmed = (name || '').trim();
    if (!trimmed) return null;
    try {
      const ref = db.ref(`events/${eventId}/tabs`).push();
      await ref.set({
        name: trimmed, status: 'open',
        createdAt: Date.now(), createdBy: user.name,
        items: {},
      });
      return ref.key;
    } catch (e) { showToast('Erreur', 'error'); return null; }
  };

  const addToTab = async (tabId, drinkId) => {
    if (!isOnline || !db) { showToast('Hors ligne — ajout impossible', 'error'); return false; }
    const drink = drinks.find(d => d.id === drinkId);
    if (!drink) return false;
    try {
      // Ajout / increment de l'item dans l'ardoise (clé = drinkId pour fusion)
      const itemRef = db.ref(`events/${eventId}/tabs/${tabId}/items/${drinkId}`);
      await itemRef.transaction(current => {
        if (!current) return { drinkId, name: drink.name, price: drink.price, quantity: 1, lastAddedAt: Date.now(), addedBy: user.name };
        return { ...current, quantity: (current.quantity || 0) + 1, lastAddedAt: Date.now(), addedBy: user.name };
      });
      // Décrément du stock (comme une vente)
      const targets = getStockTargets(drink, drinks);
      const projectedStock = {};
      for (const target of targets) {
        const idx = drinks.findIndex(d => d.id === target.drinkId);
        if (idx < 0) continue;
        const dec = target.decrementPerUnit;
        projectedStock[target.drinkId] = (drinks[idx]?.stock || 0) - dec;
        try {
          await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) - dec) * 100) / 100));
        } catch (e) { console.error('stock decrement (tab) failed', e); }
      }
      _alertLowStock(projectedStock);
      return true;
    } catch (e) { showToast('Erreur', 'error'); return false; }
  };

  // Retire 1 unité de l'item de l'ardoise (et restaure le stock).
  const removeFromTab = async (tabId, drinkId) => {
    if (!isOnline || !db) { showToast('Hors ligne', 'error'); return false; }
    try {
      let removedQty = 0;
      await db.ref(`events/${eventId}/tabs/${tabId}/items/${drinkId}`).transaction(current => {
        if (!current) return null;
        if ((current.quantity || 0) <= 1) { removedQty = current.quantity || 0; return null; }
        removedQty = 1;
        return { ...current, quantity: current.quantity - 1 };
      });
      const drink = drinks.find(d => d.id === drinkId);
      if (drink && removedQty > 0) {
        const targets = getStockTargets(drink, drinks);
        for (const target of targets) {
          const idx = drinks.findIndex(d => d.id === target.drinkId);
          if (idx < 0) continue;
          const inc = target.decrementPerUnit * removedQty;
          try {
            await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) + inc) * 100) / 100));
          } catch (e) { console.error('stock restore (tab) failed', e); }
        }
      }
      return true;
    } catch (e) { showToast('Erreur', 'error'); return false; }
  };

  // Clôture une ardoise : crée un enregistrement de vente, marque l'ardoise comme fermée.
  // PAS de re-décrément du stock (déjà fait au moment des ajouts).
  const closeTab = async (tabId, paymentMethodId, discountValue) => {
    if (!isOnline || !db || !user) { showToast('Hors ligne', 'error'); return false; }
    const tab = tabs.find(t => t._key === tabId);
    if (!tab || tab.status !== 'open') { showToast('Ardoise introuvable', 'error'); return false; }
    const itemsArr = Object.values(tab.items || {}).filter(i => (i.quantity || 0) > 0);
    if (itemsArr.length === 0) { showToast('Ardoise vide', 'error'); return false; }
    const cartList = itemsArr.map(it => ({ id: it.drinkId, name: it.name, price: it.price, qty: it.quantity || 0 }));
    const sale = _buildSale(cartList, paymentMethodId, discountValue);
    sale.tabId = tabId; sale.tabName = tab.name;
    try {
      const ref = db.ref(`events/${eventId}/sales`).push();
      await ref.set(sale);
      await db.ref(`events/${eventId}/tabs/${tabId}`).update({
        status: 'closed', closedAt: Date.now(), closedBy: user.name,
        paymentMethod: paymentMethodId, discount: sale.discount, total: sale.total,
        saleKey: ref.key,
      });
      setMyKeys(prev => [...prev, ref.key]);
      if (activeTabId === tabId) setActiveTabId(null);
      const isOffered = paymentMethodId === 'offert';
      showToast(isOffered ? `Ardoise ${tab.name} offerte` : `Ardoise ${tab.name} encaissée · ${formatPrice(sale.total)}`);
      return true;
    } catch (e) { showToast('Erreur de clôture', 'error'); return false; }
  };

  // Annule complètement une ardoise : restaure le stock pour tous ses items, supprime l'ardoise.
  const cancelTab = async (tabId) => {
    if (!isOnline || !db) { showToast('Hors ligne', 'error'); return false; }
    const tab = tabs.find(t => t._key === tabId);
    if (!tab) return false;
    const itemsArr = Object.values(tab.items || {});
    try {
      for (const it of itemsArr) {
        const drink = drinks.find(d => d.id === it.drinkId);
        if (!drink) continue;
        const targets = getStockTargets(drink, drinks);
        for (const target of targets) {
          const idx = drinks.findIndex(d => d.id === target.drinkId);
          if (idx < 0) continue;
          const inc = target.decrementPerUnit * (it.quantity || 0);
          try {
            await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => Math.max(0, Math.round(((s || 0) + inc) * 100) / 100));
          } catch (e) { console.error('stock restore (tab cancel) failed', e); }
        }
      }
      await db.ref(`events/${eventId}/tabs/${tabId}`).remove();
      if (activeTabId === tabId) setActiveTabId(null);
      showToast('Ardoise supprimée', 'info');
      return true;
    } catch (e) { showToast('Erreur', 'error'); return false; }
  };

  // Routeur central pour les taps sur les cartes de boisson.
  // Priorité : ardoise active > mode rapide > panier classique.
  const handleDrinkTap = (drinkId) => {
    if (activeTabId) {
      addToTab(activeTabId, drinkId);
      return;
    }
    if (quickMode) { quickSale(drinkId); return; }
    addToCart(drinkId);
  };

  const updateDrink = async (id, updates) => {
    const idx = drinks.findIndex(d => d.id === id);
    if (idx < 0) return;
    const updated = { ...drinks[idx], ...updates };
    setDrinks(prev => prev.map(d => d.id === id ? updated : d));
    if (db) await db.ref(`events/${eventId}/drinks/${idx}`).set(updated);
  };
  const addDrink = async (drink) => {
    const newDrink = { ...drink, id: drink.id || genId() };
    const updated = [...drinks, newDrink];
    setDrinks(updated);
    if (db) await db.ref(`events/${eventId}/drinks`).set(updated);
  };
  const removeDrinkById = async (id) => {
    const updated = drinks.filter(d => d.id !== id);
    setDrinks(updated);
    if (db) await db.ref(`events/${eventId}/drinks`).set(updated);
  };
  const resetAll = async () => {
    if (db) await db.ref(`events/${eventId}/sales`).remove();
    setMyKeys([]); setCart({}); setConfirmReset(false);
    showToast('Toutes les ventes effacées', 'info');
  };
  const resetDrinksToDefault = async () => {
    setDrinks(DEFAULT_DRINKS);
    if (db) await db.ref(`events/${eventId}/drinks`).set(DEFAULT_DRINKS);
    setConfirmDrinksReset(false);
    showToast('Menu par défaut rechargé', 'success');
  };
  const adjustStock = async (drinkId, delta) => {
    const idx = drinks.findIndex(d => d.id === drinkId);
    if (idx < 0 || !db) return;
    try {
      // Arrondi à 2 décimales pour éviter les artefacts IEEE-754 (0.6999999... etc.)
      await db.ref(`events/${eventId}/drinks/${idx}/stock`).transaction(s => {
        const next = Math.max(0, (s || 0) + delta);
        return Math.round(next * 100) / 100;
      });
    } catch (e) { showToast('Erreur stock', 'error'); }
  };
  const setStock = async (drinkId, value) => {
    const idx = drinks.findIndex(d => d.id === drinkId);
    if (idx < 0 || !db) return;
    const v = Math.max(0, parseFloat(value) || 0);
    try {
      await db.ref(`events/${eventId}/drinks/${idx}/stock`).set(v);
    } catch (e) { showToast('Erreur stock', 'error'); }
  };

  // === Sauvegarde auto (Lot 1) ===
  // Snapshot silencieux dans localStorage toutes les BACKUP_INTERVAL_MS,
  // uniquement côté admin (le téléphone admin sert de copie de secours).
  useEffect(() => {
    if (!user || user.role !== 'admin') return;
    if (isLoading) return;
    const doBackup = () => {
      try {
        const payload = {
          timestamp: Date.now(),
          version: 1,
          drinks,
          sales: sales.reduce((acc, s) => { if (s._key) acc[s._key] = (({ _key, ...rest }) => rest)(s); return acc; }, {}),
        };
        const json = JSON.stringify(payload);
        localStorage.setItem(eventStorageKey(eventId, 'backup'), json);
        setLastBackup(payload.timestamp);
      } catch (e) {
        // Quota dépassé ou autre — on ignore silencieusement
        console.warn('backup failed', e);
      }
    };
    // Premier backup immédiat puis périodique
    doBackup();
    const id = setInterval(doBackup, BACKUP_INTERVAL_MS);
    return () => clearInterval(id);
  }, [user, isLoading, drinks, sales]);

  const downloadBackup = () => {
    let raw;
    try { raw = localStorage.getItem(eventStorageKey(eventId, 'backup')); }
    catch { showToast('Impossible de lire la sauvegarde', 'error'); return; }
    if (!raw) { showToast('Aucune sauvegarde locale', 'error'); return; }
    const blob = new Blob([raw], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `caterii-backup-${new Date().toISOString().slice(0,16).replace(':','-')}.json`;
    a.click();
    URL.revokeObjectURL(url);
    showToast('Sauvegarde téléchargée');
  };

  const restoreFromBackup = async () => {
    if (!db) return;
    let raw;
    try { raw = localStorage.getItem(eventStorageKey(eventId, 'backup')); }
    catch { showToast('Lecture impossible', 'error'); return; }
    if (!raw) { showToast('Aucune sauvegarde locale', 'error'); return; }
    let parsed;
    try { parsed = JSON.parse(raw); }
    catch { showToast('Sauvegarde corrompue', 'error'); return; }
    if (!parsed || !Array.isArray(parsed.drinks)) {
      showToast('Sauvegarde invalide', 'error'); return;
    }
    try {
      await db.ref(`events/${eventId}/drinks`).set(parsed.drinks);
      await db.ref(`events/${eventId}/sales`).set(parsed.sales || {});
      showToast('Restauration terminée');
    } catch (e) {
      console.error('restore failed', e);
      showToast('Erreur de restauration', 'error');
    }
  };

  // Construit le contenu CSV à partir d'un tableau de ventes et la liste de boissons,
  // et déclenche son téléchargement. Utilisé pour l'export courant et pour les archives.
  const _downloadCSV = (salesArr, drinksList, filename) => {
    const lines = ['heure;boisson;categorie;quantite;prix_unitaire;total_ligne;paiement;remise;encaisse_total;barman'];
    (salesArr || []).forEach(sale => {
      const isOffered = sale.paymentMethod === 'offert';
      const discount = sale.discount || 0;
      (sale.items || []).forEach(item => {
        const d = (drinksList || []).find(d => d.id === item.drinkId);
        const cat = d ? (CATEGORIES[d.category]?.label || '') : '';
        const lineFull = item.price * item.quantity;
        const lineCharged = isOffered ? 0 : lineFull * (1 - discount);
        lines.push([
          new Date(sale.timestamp).toLocaleString('fr-BE'),
          item.name, cat, item.quantity,
          item.price.toFixed(2),
          lineFull.toFixed(2),
          PAYMENT_BY_ID[sale.paymentMethod]?.label || sale.paymentMethod,
          isOffered ? 'OFFERT' : (discount > 0 ? `${(discount * 100).toFixed(0)}%` : ''),
          lineCharged.toFixed(2),
          sale.userName || '',
        ].join(';'));
      });
    });
    const blob = new Blob(['\ufeff' + lines.join('\n')], { type: 'text/csv;charset=utf-8;' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  };

  const exportCSV = () => {
    _downloadCSV(sales, drinks, `caterii-${new Date().toISOString().slice(0,10)}.csv`);
  };

  const exportArchiveCSV = (archive) => {
    const salesArr = Object.values(archive.sales || {});
    salesArr.sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));
    const safeName = (archive.eventName || 'evenement').replace(/[^a-z0-9]+/gi, '-').toLowerCase();
    const stamp = new Date(archive.closedAt || Date.now()).toISOString().slice(0,10);
    _downloadCSV(salesArr, archive.drinks || drinks, `caterii-${safeName}-${stamp}.csv`);
  };

  // Clôture d'un événement : archive sales + drinks + recompte, puis remet à zéro.
  // - eventName : libellé optionnel
  // - recompte : tableau optionnel { drinkId, drinkName, expected, counted, variance, varianceValue }
  // - resetStock : si true, tous les stocks sont remis à 0 ; sinon les niveaux actuels sont gardés
  const closeEvent = async ({ eventName, recompte, resetStock }) => {
    if (!db || !user || user.role !== 'admin') return false;
    const closedAt = Date.now();
    // Snapshot lisible des ventes au format objet (key → sale)
    const salesSnap = sales.reduce((acc, s) => {
      if (s._key) {
        const { _key, ...rest } = s;
        acc[_key] = rest;
      }
      return acc;
    }, {});
    const startedAt = sales.length > 0 ? Math.min(...sales.map(s => s.timestamp || closedAt)) : closedAt;
    const endedAt   = sales.length > 0 ? Math.max(...sales.map(s => s.timestamp || closedAt)) : closedAt;
    const totalCollected = sales.reduce((s, x) => s + (x.total || 0), 0);
    const archive = {
      closedAt,
      closedBy: user.name,
      eventName: eventName || '',
      startedAt, endedAt,
      salesCount: sales.length,
      totalCollected,
      drinks: drinks, // snapshot complet (avec stock final, photos non incluses)
      sales: salesSnap,
      recompte: Array.isArray(recompte) && recompte.length > 0 ? recompte : null,
    };
    try {
      // 1. Téléchargement immédiat du CSV (avant tout reset, au cas où)
      _downloadCSV(sales, drinks, `caterii-cloture-${new Date(closedAt).toISOString().slice(0,16).replace(':','-')}.csv`);
      // 2. Archive Firebase
      const aRef = db.ref(`events/${eventId}/archives`).push();
      await aRef.set(archive);
      // 3. Reset des ventes
      await db.ref(`events/${eventId}/sales`).remove();
      // 4. Reset éventuel du stock (uniquement les items suivis, on garde la structure du menu)
      if (resetStock) {
        const cleared = drinks.map(d => d.stockEnabled ? { ...d, stock: 0 } : d);
        await db.ref(`events/${eventId}/drinks`).set(cleared);
      }
      // 5. Reset local
      setMyKeys([]);
      setCart({});
      setCartOpen(false);
      setDiscount(0);
      showToast(`Événement clôturé · ${formatPrice(totalCollected)}`);
      return true;
    } catch (e) {
      console.error('closure failed', e);
      showToast('Erreur de clôture', 'error');
      return false;
    }
  };

  const deleteArchive = async (archiveKey) => {
    if (!db || !archiveKey) return;
    try { await db.ref(`events/${eventId}/archives/${archiveKey}`).remove(); showToast('Archive supprimée', 'info'); }
    catch (e) { showToast('Erreur', 'error'); }
  };

  if (firebaseError) return <FirebaseErrorScreen error={firebaseError} />;
  if (!authReady || route === 'loading') return <LoadingScreen label={creatingEvent ? 'Création du comptoir…' : 'Chargement…'} />;
  if (route === 'landing') return <LandingPage onSignin={() => setRoute('ownerAuth')} />;
  if (route === 'ownerAuth') return <OwnerAuthScreen onBack={() => setRoute('landing')} />;
  if (route === 'eventNotFound') return <EventNotFoundScreen onBack={handleSwitchEvent} />;
  if (route === 'eventPicker') {
    return <EventPickerScreen
      user={firebaseUser}
      events={userEvents}
      onPick={(id) => { setEventId(id); setCurrentEventId(id); setRoute('loading'); }}
      onCreate={() => setRoute('eventCreate')}
      onSignout={handleOwnerSignout}
    />;
  }
  if (route === 'eventCreate') {
    return <EventCreateScreen
      user={firebaseUser}
      busy={creatingEvent}
      onCreate={handleCreateEvent}
      onCancel={() => setRoute('eventPicker')}
    />;
  }
  if (route === 'barmanLogin') {
    return <BarmanJoinScreen
      eventName={eventMeta?.name}
      members={eventMeta?.members || []}
      onLogin={handleMemberLogin}
      onLeaveEvent={firebaseUser?.isAnonymous ? null : handleSwitchEvent}
    />;
  }
  // route === 'app' — sécurité au cas où user/eventId pas encore prêts
  if (!user || !eventId) return <LoadingScreen />;
  if (isLoading) return <LoadingScreen />;

  return (
    <div className="min-h-screen pb-32">
      <header className="sticky top-0 z-30 bg-stone-50/90 backdrop-blur-md border-b border-stone-200">
        <div className="px-4 pt-3 pb-3 flex items-center justify-between">
          <div className="flex flex-col gap-1 min-w-0">
            <div className="flex items-baseline gap-2">
              <div className="font-display font-extrabold text-2xl text-stone-900 leading-none tracking-tight">Caterii</div>
              <div className={`flex items-center gap-1 text-xs ${syncStatus === 'online' ? 'text-emerald-600' : 'text-rose-600'}`}>
                <Icon name={syncStatus === 'online' ? 'Wifi' : 'WifiOff'} className="w-3 h-3" />
              </div>
              {pendingSales.length > 0 && (
                <button onClick={() => setView('config')} className="flex items-center gap-1 bg-amber-100 text-amber-800 text-[10px] font-bold px-1.5 py-0.5 rounded-full" title="Ventes en attente de synchronisation">
                  <Icon name="CloudOff" className="w-3 h-3" /> {pendingSales.length}
                </button>
              )}
            </div>
            {eventMeta?.name && (
              <div className="text-xs text-stone-600 font-semibold truncate leading-tight">{eventMeta.name}</div>
            )}
            <button
              onClick={() => setConfirm({
                title: 'Se déconnecter ?',
                body: `Tu pourras te reconnecter avec ton mot de passe à tout moment. Le panier en cours sera vidé.`,
                confirmLabel: 'Déconnecter',
                onConfirm: () => { handleMemberLogout(); setCart({}); }
              })}
              className="flex items-center gap-1.5 text-[10px] uppercase tracking-widest text-stone-500 hover:text-stone-900 leading-none"
            >
              <span className="font-bold">{user.name}</span>
              {user.role === 'admin' && <span className="bg-orange-100 text-orange-700 px-1.5 py-0.5 rounded font-bold text-[9px]">ADMIN</span>}
              <Icon name="X" className="w-2.5 h-2.5 opacity-50" strokeWidth={2.5} />
            </button>
          </div>
          <div className={`text-right shrink-0 ${flashTotal ? 'flash-success rounded-md px-2' : ''}`}>
            <div className="text-[10px] uppercase tracking-widest text-stone-500 leading-none font-semibold">Recette</div>
            <div className="font-mono text-2xl font-bold text-stone-900 tabular-nums leading-none mt-1">{formatPrice(totalRevenue)}</div>
          </div>
        </div>
      </header>

      <main>
        {tabsScreenOpen ? (
          <TabsScreen
            tabs={tabs} activeTabId={activeTabId} setActiveTabId={setActiveTabId}
            createTab={createTab} addToTab={addToTab} removeFromTab={removeFromTab}
            closeTab={closeTab} cancelTab={cancelTab} setTabsScreenOpen={setTabsScreenOpen}
            isOnline={isOnline} drinks={displayDrinks} />
        ) : (view === 'sales' || user.role !== 'admin') ? (
          <SalesView drinks={displayDrinks} photos={photos} cart={cart} onDrinkTap={handleDrinkTap}
            canUndo={user.role === 'admin' && myKeys.length > 0} undoLastSale={undoLastSale}
            activeCategory={activeCategory} setActiveCategory={setActiveCategory}
            quickMode={quickMode} toggleQuickMode={toggleQuickMode}
            lowStockCount={lowStockCount} setView={setView} isAdmin={user.role === 'admin'}
            tabs={tabs} activeTabId={activeTabId} setActiveTabId={setActiveTabId}
            setTabsScreenOpen={setTabsScreenOpen} isOnline={isOnline} />
        ) : null}
        {user.role === 'admin' && view === 'stock' && !tabsScreenOpen && <StockView drinks={displayDrinks} photos={photos} setDrinkPhoto={setDrinkPhoto} addDrink={addDrink} updateDrink={updateDrink} adjustStock={adjustStock} setStock={setStock} />}
        {user.role === 'admin' && view === 'stats' && !tabsScreenOpen && <StatsView allSales={sales} mySalesCount={myKeys.length} sessionId={sessionId} drinks={displayDrinks} cancelSale={cancelSale} tabs={tabs} pendingSales={pendingSales} />}
        {user.role === 'admin' && view === 'config' && !tabsScreenOpen && (
          <ConfigView drinks={displayDrinks} photos={photos} setDrinkPhoto={setDrinkPhoto} updateDrink={updateDrink} addDrink={addDrink} removeDrink={removeDrinkById}
            editingDrink={editingDrink} setEditingDrink={setEditingDrink}
            exportCSV={exportCSV} confirmReset={confirmReset} setConfirmReset={setConfirmReset}
            resetAll={resetAll} sessionId={sessionId} allSalesCount={sales.length} myKeysCount={myKeys.length}
            resetDrinksToDefault={resetDrinksToDefault} confirmDrinksReset={confirmDrinksReset} setConfirmDrinksReset={setConfirmDrinksReset}
            adjustStock={adjustStock} setStock={setStock}
            lastBackup={lastBackup} downloadBackup={downloadBackup} restoreFromBackup={restoreFromBackup}
            archives={archives} closeEvent={closeEvent} exportArchiveCSV={exportArchiveCSV} deleteArchive={deleteArchive}
            pendingSales={pendingSales} setPendingSales={setPendingSales} syncPendingSales={syncPendingSales} isOnline={isOnline}
            eventId={eventId} eventMeta={eventMeta} firebaseUser={firebaseUser}
            updateEventMembers={updateEventMembers} updateEventName={updateEventName} deleteEvent={deleteEvent}
            onSwitchEvent={handleSwitchEvent} onOwnerSignout={handleOwnerSignout} />
        )}
      </main>

      {cartCount > 0 && !quickMode && !activeTabId && !tabsScreenOpen && (
        <CartDrawer cartItems={cartItems} cartSubtotal={cartSubtotal} cartTotal={cartTotal} cartCount={cartCount}
          cartOpen={cartOpen} setCartOpen={setCartOpen}
          addToCart={addToCart} decrementCart={decrementCart}
          clearCart={clearCart} completeSale={completeSale}
          discount={discount} setDiscount={setDiscount} />
      )}

      <nav className="fixed bottom-0 left-0 right-0 z-20 bg-white/95 backdrop-blur-md border-t border-stone-200">
        <div className={`grid ${user.role === 'admin' ? 'grid-cols-4' : 'grid-cols-1'} max-w-md mx-auto`}>
          <TabButton active={view === 'sales'} onClick={() => setView('sales')} iconName="ShoppingBag" label="Ventes" />
          {user.role === 'admin' && <>
            <TabButton active={view === 'stock'} onClick={() => setView('stock')} iconName="Package" label="Stock" />
            <TabButton active={view === 'stats'} onClick={() => setView('stats')} iconName="ChartLine" label="Stats" />
            <TabButton active={view === 'config'} onClick={() => setView('config')} iconName="Settings" label="Config" />
          </>}
        </div>
      </nav>

      {toast && (
        <div className="fixed top-20 left-1/2 -translate-x-1/2 z-50 pop-in">
          <div className={`px-4 py-2 rounded-full font-semibold text-sm shadow-lg ${
            toast.type === 'success' ? 'bg-emerald-500 text-white' :
            toast.type === 'error' ? 'bg-rose-500 text-white' :
            'bg-stone-800 text-white'
          }`}>{toast.message}</div>
        </div>
      )}

      <ConfirmModal
        open={!!confirm}
        title={confirm?.title}
        body={confirm?.body}
        confirmLabel={confirm?.confirmLabel}
        confirmVariant={confirm?.confirmVariant}
        onConfirm={() => { confirm?.onConfirm?.(); setConfirm(null); }}
        onCancel={() => setConfirm(null)}
      />
    </div>
  );
}

// === Montage React + masquage du boot loader ===
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
setTimeout(() => {
  const bl = document.getElementById('boot-loader');
  if (bl) bl.classList.add('hidden');
  setTimeout(() => bl && bl.remove(), 400);
}, 100);
