// ─── App (root) ────────────────────────────────────────────────────────

function App() {
  const t = useT();
  const [currentView, setCurrentView]       = useState('home');   // 'home'|'map'|'passport'|'scanner'|'leaderboard'
  const [user, setUser]                     = useState(null);
  const [showRegisterModal, setShowRegisterModal] = useState(false); // false | 'passport' | 'scan'
  const [activeFilter, setActiveFilter]     = useState('all');
  const [scanStep, setScanStep]             = useState(0);
  const [stamps, setStamps]                 = useState({});
  const [activeTab, setActiveTab]           = useState('gran-viajero');
  const [selectedBusiness, setSelectedBusiness] = useState(null);
  const [bizCard, setBizCard] = useState(null);
  const [reward, setReward]                 = useState(null);       // business for celebration modal
  const [scannerDirect, setScannerDirect]   = useState(false);
  const [showBusiness, setShowBusiness] = useState(() => !!localStorage.getItem('helixBiz'));
  const [zone, setZone]                     = useState(null);
  const [bizReady, setBizReady]             = useState(false);
  const [showZonePicker, setShowZonePicker] = useState(false);
  const [activeZones, setActiveZones] = useState([]);
  const [bizVersion, setBizVersion] = useState(0);
  const [isCycleWinner, setIsCycleWinner] = useState([]);
  const [pendingRewards, setPendingRewards] = useState([]);
  const [winnerDismissed, setWinnerDismissed] = useState(() => !!localStorage.getItem('helixWinnerDismissed'));
  const rewardCheckRef = useRef(null);
  const autoScanBizRef = useRef(new URLSearchParams(window.location.search).get('b'));

  useEffect(() => {
  const saved = localStorage.getItem('helixZone');
  const fallback = () => { setShowZonePicker(true); setBizReady(true); };

  // Usar zona guardada de inmediato para no bloquear el render
  if (saved) setZone(saved);
  else setShowZonePicker(true);

  if (!navigator.geolocation) {
    if (!saved) setBizReady(true);
    return;
  }

  // Siempre correr geoloc: si hay zona guardada, verifica en background;
  // si no hay, intenta auto-detectar
  navigator.geolocation.getCurrentPosition(
    (pos) => {
      const { latitude, longitude } = pos.coords;
      const zonesData = window.ZONES?.length ? window.ZONES : ZONES;

      const closest = zonesData.reduce((best, z) => {
        const d = Math.hypot(z.lat - latitude, z.lng - longitude);
        return d < best.d ? { z, d } : best;
      }, { z: zonesData[0], d: Infinity });

      const nearAZone = closest.d < 1.0; // ~110km — dentro de zona

      if (!saved) {
        if (nearAZone) {
          setZone(closest.z.id);
          localStorage.setItem('helixZone', closest.z.id);
          setShowZonePicker(false);
        }
        // Si está lejos de todas las zonas, deja el picker abierto
      } else if (nearAZone && closest.z.id !== saved) {
        // Zona diferente a la guardada → actualizar silenciosamente
        setZone(closest.z.id);
        localStorage.setItem('helixZone', closest.z.id);
      }
      // Si está lejos de todas las zonas con zona guardada → mantener la guardada
    },
    () => { if (!saved) fallback(); },
    { timeout: 6000, maximumAge: 300000 }
  );
}, []);

  useEffect(() => {
    fetch(`${API}/zones`).then(r => r.json()).then(zones => {
    if (Array.isArray(zones) && zones.length) {
      window.ZONES = zones;
      setActiveZones(zones.map(z => z.id));
    }
  }).catch(() => {});
  }, []);

  useEffect(() => {
    if (!zone) return;
    setBizReady(false);
    loadBusinesses(zone).then(() => {
      setBizReady(true);
      setBizVersion(v => v + 1);
      if (window.BUSINESSES && window.BUSINESSES.length > 0) {
        localStorage.setItem('helixLastZoneWithBiz', zone);
      }
    });
  }, [zone]);

  useEffect(() => {
    const p = new URLSearchParams(window.location.search);
    const bizId = p.get('b');
    const bizZone = p.get('z');
    if (!bizId) return;
    const currentZone = localStorage.getItem('helixZone') || 'la-paz';
    if (bizZone && bizZone !== currentZone) {
      localStorage.setItem('helixZone', bizZone);
      window.location.reload();
      return;
    }
    if (!bizReady) return;
    if (window.BUSINESSES?.length === 0) return;
    const biz = window.BUSINESSES?.find(b => b.id === bizId);
    if (!biz) return;
    window.history.replaceState({}, '', '/');
    setSelectedBusiness(biz);
    if (!user) { setShowRegisterModal('scan'); return; }
    setScanStep(2);
    setCurrentView('scanner');
  }, [bizReady, bizVersion]);

  // ─── Rehydrate from localStorage ────────────────────────────────
  useEffect(() => {
    try {
      const u = localStorage.getItem('helixUser');
      if (u) setUser(JSON.parse(u));
      const s = localStorage.getItem('helixStamps');
      if (s) setStamps(JSON.parse(s));
    } catch (_) {}
  }, []);

  useEffect(() => {
    if (!user?.name) return;
    Promise.all(ZONES.map(z =>
      fetch(`${API}/winners?zone=${z.id}`).then(r => r.json()).catch(() => [])
    )).then(results => {
      const wonZones = results.map((winners, i) => {
        if (!Array.isArray(winners) || winners.length === 0) return null;
        if (winners[0].user_name === user.name) return ZONES[i].label;
        return null;
      }).filter(Boolean);
      if (wonZones.length > 0) {
        setIsCycleWinner(wonZones);
      }
    });
  }, [user]);

  // persist stamps
  useEffect(() => {
    try { localStorage.setItem('helixStamps', JSON.stringify(stamps)); } catch(_){}
  }, [stamps]);

  // Sincronizar stamps desde DB al abrir pasaporte
  useEffect(() => {
    if (currentView !== 'passport' || !user?.id) return;
    fetch(`${API}/visits?userId=${user.id}&zone=${zone || 'la-paz'}`)
      .then(r => r.json())
      .then(visits => {
        if (!Array.isArray(visits)) return;
        const loaded = {};
        visits.forEach(v => {
          loaded[v.business_id] = {
            visits: v.visits,
            unlocked: true,
            lastVisit: new Date().toISOString()
          };
        });
        setStamps(loaded);
        try { localStorage.setItem('helixStamps', JSON.stringify(loaded)); } catch(_){}
      })
      .catch(() => {});
    fetch(`${API}/rewards?userId=${user.id}`)
    .then(r => r.json())
    .then(data => { if (Array.isArray(data)) setPendingRewards(data); })
    .catch(() => {});
  }, [currentView]);

  // Reset scanner on entering scanner view fresh
  useEffect(() => {
    if (currentView !== 'scanner') return;
    // Reset to step 1 if step is currently 0 and we just arrived from a Reclamar
    // (we set scanStep from the claim handler, so leave it)
  }, [currentView]);

  // Scroll top on view change
  useEffect(() => {
    window.scrollTo({ top: 0, behavior: 'instant' in window ? 'instant' : 'auto' });
  }, [currentView]);

  // ─── Handlers ───────────────────────────────────────────────────
  const handleClaim = (business) => {
    setBizCard(business);
  };

  const handleScanFromCard = (business) => {
    setBizCard(null);
    setSelectedBusiness(business);
    if (!user) {
      setShowRegisterModal('scan');
      return;
    }
    setScanStep(1);
    setCurrentView('scanner');
  };

  const handleRegister = async ({ name, phone, pin }) => {
    try {
      const res = await fetch(`${API}/register`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, phone, pin })
      });
      const data = await res.json();
      if (data.error) return;
      const newUser = { id: data.id, name: data.name || name, phone, pin, poll_token: data.poll_token, public_id: data.public_id };
      setUser(newUser);
      try { localStorage.setItem('helixUser', JSON.stringify(newUser)); } catch(_){}
      // Cargar stamps desde DB
      try {
        const visits = await fetch(`${API}/visits?userId=${newUser.id}`).then(r => r.json());
        if (Array.isArray(visits)) {
          const loaded = {};
          visits.forEach(v => {
            loaded[v.business_id] = {
              visits: v.visits,
              unlocked: true,
              lastVisit: new Date().toISOString()
            };
          });
          setStamps(loaded);
          localStorage.setItem('helixStamps', JSON.stringify(loaded));
        }
      } catch(_) {}
    } catch(_) {
      return;
    }
    const ctx = showRegisterModal;
    setShowRegisterModal(false);
    if (ctx === 'scan') {
      setScanStep(selectedBusiness ? 2 : 1);
      setCurrentView('scanner');
    } else if (ctx === 'passport') {
      setCurrentView('passport');
    }
  };

  const handleLogout = () => {
    setUser(null);
    setStamps({});
    try {
      localStorage.removeItem('helixUser');
      localStorage.removeItem('helixStamps');
    } catch(_){}
    setCurrentView('home');
  };

  // ── Recarga stamps + rewards desde la BD y actualiza el estado global ──────
  const refreshUserData = async (forZone) => {
    if (!user?.id) return;
    const z = forZone || zone || localStorage.getItem('helixZone') || 'la-paz';
    try {
      const [dbVisits, rewards] = await Promise.all([
        fetch(`${API}/visits?userId=${user.id}&zone=${z}`).then(r => r.json()),
        fetch(`${API}/rewards?userId=${user.id}`).then(r => r.json()),
      ]);
      if (Array.isArray(dbVisits)) {
        const loaded = {};
        dbVisits.forEach(v => {
          loaded[v.business_id] = { visits: v.visits, unlocked: true, lastVisit: new Date().toISOString() };
        });
        setStamps(loaded);
        try { localStorage.setItem('helixStamps', JSON.stringify(loaded)); } catch(_) {}
      }
      if (Array.isArray(rewards)) setPendingRewards(rewards);
    } catch(_) {}
  };

  // Arranca el fetch de visitas + earn en cuanto el QR es verificado (background).
  // Si hay promo, esto corre mientras el usuario lee el banner — ya está listo al cerrar.
  const handleVerified = (b) => {
    if (!b || !user?.id) return;
    const z = window.__helixZoneChange || zone || localStorage.getItem('helixZone') || 'la-paz';
    rewardCheckRef.current = (async () => {
      await new Promise(r => setTimeout(r, 800));
      try {
        const dbVisits = await fetch(`${API}/visits?userId=${user.id}&zone=${z}`).then(r => r.json());
        if (Array.isArray(dbVisits)) {
          const found = dbVisits.find(v => v.business_id === b.id);
          if (found) {
            const realVisits = found.visits;
            const goal = parseInt(b.rewardGoal || b.reward_goal) || 0;
            const reached = goal > 0 && realVisits > 0 && realVisits % goal === 0;
            if (reached) {
              try {
                await fetch(`${API}/rewards/earn`, {
                  method: 'POST',
                  headers: { 'Content-Type': 'application/json' },
                  body: JSON.stringify({ userId: user.id, businessId: b.id })
                });
              } catch(_) {}
            }
            return { realVisits, reached };
          }
        }
      } catch(_) {}
      return { realVisits: 0, reached: false };
    })();
  };

  const handleScanComplete = async () => {
    const newZone = window.__helixZoneChange || zone;
    if (window.__helixZoneChange) {
      setZone(window.__helixZoneChange);
      window.__helixZoneChange = null;
    }
    const b = selectedBusiness || window.__helixScannedBiz;
    setScanStep(0);
    window.__helixScannedBiz = null;

    setCurrentView('passport');
    setActiveTab('gran-viajero');

    if (!b) return;

    const pending = rewardCheckRef.current;
    rewardCheckRef.current = null;
    const result = pending ? await pending : null;

    if (result) {
      if (result.realVisits > 0) {
        setStamps(prev => ({
          ...prev,
          [b.id]: { visits: result.realVisits, unlocked: true, lastVisit: new Date().toISOString() }
        }));
      }
      if (result.reached) setReward(b);
    } else {
      // fallback: sin promo, el check ya corrió en handleVerified igualmente
    }
  };

  // Al cerrar el modal: recarga datos (barra en 0 + reward en inventario) → navega a VIP
  const closeReward = async () => {
    setReward(null);
    const z = zone || localStorage.getItem('helixZone') || 'la-paz';
    await refreshUserData(z);
    setCurrentView('passport');
    setActiveTab('cliente-vip');
  };

  // ─── Total stamps for leaderboard user row ─────────────────────
  const totalUniqueSpots = useMemo(
    () => Object.values(stamps).filter(s => s.unlocked).length,
    [stamps]
  );
  const totalVisits = useMemo(
    () => Object.values(stamps).reduce((acc, s) => acc + (s.visits || 0), 0),
    [stamps]
  );

  // ─── Renderers ─────────────────────────────────────────────────
  const main = (() => {
    switch (currentView) {
      case 'home':
        return (
          <HomeView
            user={user}
            zone={zone}
            onChangeZone={() => setShowZonePicker(true)}
            onClaim={handleClaim}
            onSeeLeaderboard={() => setCurrentView('leaderboard')}
            onSeeMap={() => setCurrentView('map')}
            onOpenBusiness={() => setShowBusiness(true)}
            onRegister={() => setShowRegisterModal('passport')}
          />
        );
      case 'map':
        return (
          <MapView
            activeFilter={activeFilter}
            setActiveFilter={setActiveFilter}
            onBack={() => setCurrentView('home')}
            onProfile={() => {
              if (!user) { setShowRegisterModal('passport'); return; }
              setCurrentView('passport');
            }}
            onClaim={handleClaim}
            user={user}
          />
        );
      case 'passport':
        return (
          <PassportView
            user={user}
            stamps={stamps}
            activeTab={activeTab}
            setActiveTab={setActiveTab}
            onBack={() => setCurrentView('home')}
            onProfile={() => {}}
            onActivate={() => setShowRegisterModal('passport')}
            onLogout={handleLogout}
            pendingRewards={pendingRewards}
          />
        );
      case 'scanner':
        return (
          <ScannerView
            scanStep={scannerDirect ? (scanStep === 0 ? 1 : scanStep) : scanStep}
            setScanStep={setScanStep}
            selectedBusiness={selectedBusiness}
            onVerified={handleVerified}
            onComplete={handleScanComplete}
            user={user}
            onBack={() => { setScanStep(0); setScannerDirect(false); setCurrentView('home'); }}
            onProfile={() => {
              if (!user) { setShowRegisterModal('passport'); return; }
              setCurrentView('passport');
            }}
          />
        );
      case 'leaderboard':
        return (
          <LeaderboardView
            user={user}
            totalUniqueSpots={totalUniqueSpots}
            totalVisits={totalVisits}
            onClaim={handleClaim}
            onBack={() => setCurrentView('home')}
            onProfile={() => {
              if (!user) { setShowRegisterModal('passport'); return; }
              setCurrentView('passport');
            }}
          />
        );
      default:
        return null;
    }
  })();

  if (!zone && !showZonePicker) return (
    <div style={{ minHeight:'100vh', background:'var(--canvas)', display:'flex', alignItems:'center', justifyContent:'center', flexDirection:'column', gap:12, padding:'0 32px', textAlign:'center' }}>
      <div style={{ fontSize:32, marginBottom:8 }}>🗺️</div>
      <div style={{ fontWeight:800, fontSize:20, letterSpacing:'-0.02em' }}>{t('app_coming_soon_title')}</div>
          <div style={{ fontSize:14, color:'var(--muted)', marginTop:6, marginBottom:24 }}>{t('app_coming_soon_sub')}</div>
          <button onClick={() => setShowZonePicker(true)} style={{ background:'var(--teal)', color:'white', border:0, borderRadius:14, padding:'14px 24px', fontWeight:700, fontSize:14, cursor:'pointer' }}>{t('app_coming_soon_btn')}</button>
    </div>
  );
  if (!bizReady && !showZonePicker) return (
    <div style={{ minHeight:'100vh', background:'var(--canvas)', display:'flex', alignItems:'center', justifyContent:'center', flexDirection:'column', gap:12 }}>
      <svg width="28" height="28" viewBox="0 0 32 32" fill="none">
        <path d="M6 4c0 8 20 16 20 24" stroke="var(--teal)" strokeWidth="2.5" strokeLinecap="round"/>
        <path d="M6 28c0-8 20-16 20-24" stroke="var(--cyan)" strokeWidth="2.5" strokeLinecap="round"/>
      </svg>
      <div style={{ fontSize:13, color:'var(--muted)', fontWeight:600 }}>{t('passport_loading')}</div>
    </div>
  );



  return (
    <div style={{
      paddingBottom: currentView === 'home' ? 84 : 90,
      minHeight:'100vh',
      background:'var(--canvas)'
    }}>
      {bizReady && (!window.BUSINESSES || window.BUSINESSES.length === 0) ? (
        <div style={{ minHeight:'100vh', background:'var(--canvas)', display:'flex', alignItems:'center', justifyContent:'center', flexDirection:'column', gap:12, padding:'0 32px', textAlign:'center' }}>
          <div style={{ fontSize:32, marginBottom:8 }}>🗺️</div>
          <div style={{ fontWeight:800, fontSize:20, letterSpacing:'-0.02em' }}>{t('app_coming_soon_title')}</div>
          <div style={{ fontSize:14, color:'var(--muted)', marginTop:6, marginBottom:24 }}>{t('app_coming_soon_sub')}</div>
          <button onClick={() => setShowZonePicker(true)} style={{ background:'var(--teal)', color:'white', border:0, borderRadius:14, padding:'14px 24px', fontWeight:700, fontSize:14, cursor:'pointer'
          }}>{t('app_coming_soon_btn')}</button>
        </div>
      ) : (
        <div key={currentView + bizVersion}>{main}</div>
      )}

      {isCycleWinner.length > 0 && !winnerDismissed && (
        <div style={{
          position:'fixed', inset:0, zIndex:200,
          background:'rgba(15,23,42,0.85)',
          backdropFilter:'blur(8px)',
          display:'flex', alignItems:'center', justifyContent:'center',
          padding:'24px',
          animation:'fadeIn .3s ease both'
        }}>
          <div style={{
            background:'var(--canvas)',
            borderRadius:20,
            padding:'32px 24px',
            textAlign:'center',
            maxWidth:360, width:'100%'
          }}>
            <div style={{ fontSize:64, marginBottom:16 }}>🏆</div>
            <div className="label-eyebrow" style={{ color:'var(--teal)', marginBottom:8 }}>Helix Passport</div>
            <h2 className="h-section" style={{ fontSize:26, margin:'0 0 12px' }}>{t('app_winner_title')}</h2>
            <p style={{ fontSize:14, color:'var(--muted)', lineHeight:1.6, margin:'0 0 24px' }}>{t('app_winner_sub_a')} {user?.name}, {t('app_winner_sub_b')} {isCycleWinner.join(' y ')}. {t('app_winner_sub_c')}</p>
            <button onClick={() => {
              localStorage.setItem('helixWinnerDismissed', '1');
              setWinnerDismissed(true);
            }} style={{
              background:'var(--teal)', color:'white',
              border:0, borderRadius:14, padding:'14px 24px',
              fontWeight:700, fontSize:14, cursor:'pointer', width:'100%'
            }}>{t('app_winner_btn')}</button>
          </div>
        </div>
      )}

      <BottomNav
        currentView={currentView}
        user={user}
        setCurrentView={setCurrentView}
        setShowRegisterModal={setShowRegisterModal}
        setScannerDirect={setScannerDirect}
      />

      {showBusiness && <BusinessPanel onClose={() => setShowBusiness(false)}/>}

      <RegisterModal
        open={!!showRegisterModal}
        onClose={() => setShowRegisterModal(false)}
        onRegister={handleRegister}
        contextLabel={
          showRegisterModal === 'scan' ? t('app_register_ctx_scan')
          : showRegisterModal === 'passport' ? t('app_register_ctx_passport')
          : null
        }
      />

      <RewardModal open={!!reward} business={reward} onClose={closeReward}/>
      {bizCard && <BusinessCard business={bizCard} onClose={() => setBizCard(null)} onScan={handleScanFromCard} onMap={() => { const b = bizCard; window.__helixPendingBiz = b; setBizCard(null); setCurrentView('map'); setTimeout(() => { window.__helixPendingBiz = null; if (window.__helixMapSelect) window.__helixMapSelect(b); }, 50); }}/>}
      <ZonePicker open={showZonePicker} activeZones={activeZones} onSelect={(z) => { if (!z) {
        setShowZonePicker(false); return; } if (z === 'nowhere') { setShowZonePicker(false); setZone('nowhere'); return; } setShowZonePicker(false);
        if (window.BUSINESSES && window.BUSINESSES.length > 0) {
          localStorage.setItem('helixLastZoneWithBiz', zone); }
        localStorage.setItem('helixZone', z); if (z === zone) {
          setBizReady(false); loadBusinesses(z).then(() => {
            setBizReady(true); setBizVersion(v => v+1); }); }
        else { setBizReady(false); setZone(z); } }}/>
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);