// App principale

const { useState: useS, useMemo: useM, useEffect: useE, useRef: useR } = React;

// Navigation helper — expose globalement pour d'autres composants
window.navigateTo = function(hash) { window.location.hash = hash; };

function profileScore(listing, pid) {
  return listing.scoresByProfile?.[pid]?.score ?? listing.score;
}

function normalize(s) {
  return (s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '');
}

const DEFAULT_FILTERS = {
  type: "all",
  priceMin: 0,
  priceMax: 3000000,
  scoreMin: 0,
  units: "all",
  cities: [],
  neighborhoods: [],
  capRateMin: 0,           // en % (0..15)
  dscrMin1: false,
  cashFlowPositive: false,
  vsMarket: [],            // ('below'|'at'|'premium')[] — codes internes, mappés depuis 'sous le marché'/'au marché'/'prime de marché'
  daysOnMarketMin30: false,
  yearBuiltMin: null,      // number | null
  bedroomsMin: null,       // number | null
  bathroomsMin: null,      // number | null
  areaMin: null,           // number | null — en pi²
};

// Mapping interne ↔ valeurs FR du backend
const VS_MARKET_MAP = {
  below:   'sous le marché',
  at:      'au marché',
  premium: 'prime de marché',
};

function vsMarketCode(rawValue) {
  if (rawValue === 'sous le marché') return 'below';
  if (rawValue === 'au marché')      return 'at';
  if (rawValue === 'prime de marché') return 'premium';
  return null;
}

function initialFiltersForProfile(pid) {
  const base = { ...DEFAULT_FILTERS };
  switch (pid) {
    case 'cashflow_hunter':  return { ...base, scoreMin: 30, cashFlowPositive: true };
    case 'first_buyer':      return { ...base, scoreMin: 30, units: "2" };
    case 'buy_and_hold':     return { ...base, scoreMin: 40, daysOnMarketMin30: true };
    case 'value_add':        return { ...base, scoreMin: 30, capRateMin: 5, vsMarket: ['below'] };
    case 'brrrr':            return { ...base, scoreMin: 30, units: "4+", capRateMin: 4, vsMarket: ['below'] };
    case 'student_housing':  return { ...base, scoreMin: 25, units: "3" };
    default:                 return base;
  }
}

// QA helper
window._resetOnboarding = function() {
  localStorage.removeItem('vestora_onboarded');
  location.reload();
};

function App() {
  const [lang, setLang] = useS("fr");
  const [currentRoute, setCurrentRoute] = useS(window.location.hash);
  const [loginModalOpen, setLoginModalOpen] = useS(false);
  const [currentUser, setCurrentUser] = useS(() => {
    const auth = window.vestora && window.vestora.auth;
    return auth ? auth.getUser() : null;
  });

  useE(() => {
    const handler = () => setCurrentRoute(window.location.hash);
    window.addEventListener('hashchange', handler);
    return () => window.removeEventListener('hashchange', handler);
  }, []);

  // Auth Supabase — initialisation au boot + écoute changements de session
  useE(() => {
    const auth = window.vestora && window.vestora.auth;
    if (!auth) return;
    // init() lit window.VESTORA_CONFIG (URL/key vides → désactivé sans erreur bloquante)
    auth.init();
    const unsubscribe = auth.onAuthStateChange((user) => {
      setCurrentUser(user);
    });
    return unsubscribe;
  }, []);

  // Expose openModal globalement pour les composants hors React-tree (ex: PricingPage)
  useE(() => {
    window.vestora = window.vestora || {};
    window.vestora.auth = window.vestora.auth || {};
    window.vestora.auth.openModal = () => setLoginModalOpen(true);
    return () => {
      if (window.vestora?.auth?.openModal) delete window.vestora.auth.openModal;
    };
  }, []);
  const [query, setQuery] = useS("");
  // Multi-zones : tableau de {kind:'city'|'neighborhood', value} sélectionnées comme chips
  const [autocompleteValues, setAutocompleteValues] = useS([]);
  const initialProfile = (typeof window !== "undefined" && window.DEFAULT_PROFILE) || "cashflow_hunter";
  const [activeProfile, setActiveProfile] = useS(initialProfile);
  const [filters, setFilters] = useS(() => initialFiltersForProfile(initialProfile));
  const [sort, setSort] = useS("score_desc");
  const [hoveredId, setHoveredId] = useS(null);
  const [selectedListingId, setSelectedListingId] = useS(null);
  const [activeListing, setActiveListing] = useS(null);
  const [drawerOpen, setDrawerOpen] = useS(false);
  const [favs, setFavs] = useS(new Set());
  const [mobileView, setMobileView] = useS("list");
  const [mobileFiltersOpen, setMobileFiltersOpen] = useS(false);
  const [profileModalOpen, setProfileModalOpen] = useS(false);
  // userProfile — chargé depuis localStorage via UserProfileModal, maintenant en state React
  const [userProfile, setUserProfile] = useS(() => window.userProfile || window.DEFAULT_USER_PROFILE || {});

  // Compare state — max 4 biens, persisté dans localStorage
  const [compareIds, setCompareIds] = useS(() => {
    try {
      const stored = localStorage.getItem('vestora_compare');
      return stored ? JSON.parse(stored) : [];
    } catch { return []; }
  });

  const toggleCompare = (id) => {
    const sid = String(id);
    setCompareIds(prev => {
      let next;
      if (prev.includes(sid)) {
        next = prev.filter(x => x !== sid);
      } else if (prev.length < 4) {
        next = [...prev, sid];
      } else {
        return prev; // max atteint
      }
      try { localStorage.setItem('vestora_compare', JSON.stringify(next)); } catch {}
      return next;
    });
  };

  const clearCompare = () => {
    setCompareIds([]);
    try { localStorage.removeItem('vestora_compare'); } catch {}
  };

  // Onboarding: show if not yet completed
  const [showOnboarding, setShowOnboarding] = useS(
    () => !localStorage.getItem('vestora_onboarded')
  );

  const handleOnboardingComplete = (profile) => {
    localStorage.setItem('vestora_user_profile', JSON.stringify(profile));
    localStorage.setItem('vestora_onboarded', 'true');
    window.userProfile = profile;
    if (profile.strategyId) {
      changeProfile(profile.strategyId);
    }
    setShowOnboarding(false);
  };

  // Map / bounds state
  const [mapBounds, setMapBounds] = useS(null);          // bounds appliqués
  const [pendingBounds, setPendingBounds] = useS(null);  // bounds émis par la map (debouncé)
  const [showSearchArea, setShowSearchArea] = useS(false);

  const t = window.I18N[lang];

  // Disponibilité des nouveaux champs dans le dataset (>10% = afficher le filtre)
  const dataAvailability = useM(() => {
    const ls = window.LISTINGS || [];
    const total = ls.length || 1;
    return {
      yearBuilt: ls.filter(l => l.yearBuilt != null && l.yearBuilt > 0).length / total > 0.10,
      bedrooms:  ls.filter(l => l.bedrooms != null && l.bedrooms > 0).length / total > 0.10,
      bathrooms: ls.filter(l => l.bathrooms != null && l.bathrooms > 0).length / total > 0.10,
      area:      ls.filter(l => (l.grossArea != null && l.grossArea > 0) || (l.livingArea != null && l.livingArea > 0)).length / total > 0.10,
    };
  }, []);

  // Suggestions calculées une fois (city + neighborhood)
  const suggestions = useM(() => {
    const cityCounts = new Map();
    const neighCounts = new Map(); // key = "ville · quartier" pour disambiguation
    (window.LISTINGS || []).forEach(l => {
      if (l.city) cityCounts.set(l.city, (cityCounts.get(l.city) || 0) + 1);
      if (l.neighborhood && l.city) {
        const key = `${l.city}::${l.neighborhood}`;
        const cur = neighCounts.get(key) || { city: l.city, neighborhood: l.neighborhood, count: 0 };
        cur.count++;
        neighCounts.set(key, cur);
      }
    });
    return {
      cities: [...cityCounts].map(([value, count]) => ({ kind: 'city', value, count }))
        .sort((a, b) => b.count - a.count),
      neighborhoods: [...neighCounts.values()].map(x => ({
        kind: 'neighborhood', value: x.neighborhood, city: x.city, count: x.count
      })).sort((a, b) => b.count - a.count),
    };
  }, []);

  const filtered = useM(() => {
    let xs = (window.LISTINGS || []).slice();

    // Multi-zones : OR entre toutes les zones sélectionnées comme chips
    if (autocompleteValues.length > 0) {
      xs = xs.filter(l => autocompleteValues.some(av => {
        if (av.kind === 'city')         return l.city === av.value;
        if (av.kind === 'neighborhood') return l.neighborhood === av.value;
        return false;
      }));
    } else if (query.trim()) {
      const q = normalize(query);
      xs = xs.filter(l =>
        normalize(l.address).includes(q) ||
        normalize(l.neighborhood).includes(q) ||
        normalize(l.city).includes(q) ||
        normalize(l.type).includes(q)
      );
    }

    if (filters.type !== "all") xs = xs.filter(l => l.type === filters.type);
    xs = xs.filter(l => l.price >= filters.priceMin && l.price <= filters.priceMax);
    xs = xs.filter(l => profileScore(l, activeProfile) >= filters.scoreMin);

    if (filters.units !== "all") {
      if (filters.units === "4+") xs = xs.filter(l => l.units >= 4);
      else xs = xs.filter(l => l.units === parseInt(filters.units, 10));
    }

    if (filters.cities.length) {
      xs = xs.filter(l => filters.cities.includes(l.city));
    }
    if (filters.cities.length === 1 && filters.neighborhoods.length) {
      xs = xs.filter(l => filters.neighborhoods.includes(l.neighborhood));
    }
    if (filters.capRateMin > 0) {
      xs = xs.filter(l => ((l.metrics?.capRate ?? 0) * 100) >= filters.capRateMin);
    }
    if (filters.dscrMin1) {
      xs = xs.filter(l => (l.metrics?.dscr ?? 0) >= 1);
    }
    if (filters.cashFlowPositive) {
      xs = xs.filter(l => (l.metrics?.cashFlow ?? 0) > 0);
    }
    if (filters.vsMarket.length) {
      xs = xs.filter(l => {
        const code = vsMarketCode(l.marketContext?.vsMarket);
        return code && filters.vsMarket.includes(code);
      });
    }
    if (filters.daysOnMarketMin30) {
      xs = xs.filter(l => (l.daysOnMarket ?? 0) >= 30);
    }
    if (filters.yearBuiltMin) {
      xs = xs.filter(l => l.yearBuilt != null && l.yearBuilt > 0 && l.yearBuilt >= filters.yearBuiltMin);
    }
    if (filters.bedroomsMin) {
      xs = xs.filter(l => l.bedrooms != null && l.bedrooms >= filters.bedroomsMin);
    }
    if (filters.bathroomsMin) {
      xs = xs.filter(l => l.bathrooms != null && l.bathrooms >= filters.bathroomsMin);
    }
    if (filters.areaMin) {
      xs = xs.filter(l => {
        const sqft = l.grossArea ? l.grossArea * 10.7639 : (l.livingArea ? l.livingArea : null);
        return sqft != null && sqft >= filters.areaMin;
      });
    }

    // Bounds (en dernier — déjà filtré)
    if (mapBounds) {
      xs = xs.filter(l => l.lat != null && l.lng != null
        && l.lat >= mapBounds.south && l.lat <= mapBounds.north
        && l.lng >= mapBounds.west  && l.lng <= mapBounds.east);
    }

    switch (sort) {
      case "score_desc":   xs.sort((a, b) => profileScore(b, activeProfile) - profileScore(a, activeProfile)); break;
      case "price_asc":    xs.sort((a, b) => a.price - b.price); break;
      case "price_desc":   xs.sort((a, b) => b.price - a.price); break;
      case "caprate_desc": xs.sort((a, b) => (b.metrics?.capRate || 0) - (a.metrics?.capRate || 0)); break;
    }
    return xs;
  }, [query, autocompleteValues, filters, sort, activeProfile, mapBounds]);

  const openDrawer = (l) => { setActiveListing(l); setDrawerOpen(true); setSelectedListingId(l.id); };
  const closeDrawer = () => setDrawerOpen(false);

  const toggleFav = (id) => {
    setFavs(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const changeProfile = (pid) => {
    setActiveProfile(pid);
    setFilters(initialFiltersForProfile(pid));
    setQuery("");
    setAutocompleteValues([]);
    setMapBounds(null);
    setPendingBounds(null);
    setShowSearchArea(false);
  };

  const resetFilters = () => {
    setFilters(initialFiltersForProfile(activeProfile));
    setQuery("");
    setAutocompleteValues([]);
    setMapBounds(null);
    setPendingBounds(null);
    setShowSearchArea(false);
  };

  const handleBoundsChange = (b) => {
    setPendingBounds(b);
    setShowSearchArea(true);
  };

  const applySearchArea = () => {
    if (pendingBounds) setMapBounds(pendingBounds);
    setShowSearchArea(false);
  };

  // Onboarding — render fullscreen wizard before anything else
  if (showOnboarding) {
    return (
      <window.OnboardingWizard
        lang={lang}
        onComplete={handleOnboardingComplete}
      />
    );
  }

  // Hash routing : #/tarifs
  if (currentRoute === '#/tarifs') {
    return (
      <>
        <window.PricingPage
          lang={lang}
          onBack={() => window.navigateTo('#/')}
          currentUser={currentUser}
        />
        <window.LoginModal
          open={loginModalOpen}
          onClose={() => setLoginModalOpen(false)}
          lang={lang}
        />
      </>
    );
  }

  // Hash routing : #/paywall-demo (test visuel PaywallModal)
  if (currentRoute === '#/paywall-demo') {
    return (
      <window.PaywallModalDemo
        onBack={() => window.navigateTo('#/')}
      />
    );
  }

  // Hash routing : #/compare
  if (currentRoute === '#/compare') {
    return (
      <window.ComparePage
        compareIds={compareIds}
        onRemove={(id) => toggleCompare(id)}
        onClear={clearCompare}
        onBack={() => window.navigateTo('#/')}
        lang={lang}
        t={t}
        activeProfile={activeProfile}
      />
    );
  }

  // Hash routing : #/analyze/:id
  const analyzeMatch = currentRoute.match(/^#\/analyze\/(.+)$/);
  if (analyzeMatch) {
    const listingId = decodeURIComponent(analyzeMatch[1]);
    const listingForAnalyze = (window.LISTINGS || []).find(l => String(l.id) === String(listingId));
    if (listingForAnalyze) {
      return (
        <window.AnalyzePage
          listing={listingForAnalyze}
          profile={window.userProfile}
          lang={lang}
          onBack={() => window.navigateTo('#/')}
        />
      );
    }
  }

  // Détecter si on est en phase de chargement initial (LISTINGS pas encore prêt)
  const isLoading = typeof window.LISTINGS === "undefined" || window.LISTINGS === null;

  return (
    <div className="app-root">
      <window.Header
        lang={lang}
        setLang={setLang}
        t={t}
        query={query}
        setQuery={setQuery}
        autocompleteValues={autocompleteValues}
        setAutocompleteValues={setAutocompleteValues}
        suggestions={suggestions}
        activeProfile={activeProfile}
        onOpenProfileModal={() => setProfileModalOpen(true)}
        userProfile={userProfile}
        setUserProfile={setUserProfile}
        onOpenLogin={() => setLoginModalOpen(true)}
        currentUser={currentUser}
      />
      <window.HypothesesStrip
        userProfile={userProfile}
        setUserProfile={setUserProfile}
      />
      <window.ProfileSelector
        activeProfile={activeProfile}
        setActiveProfile={changeProfile}
        lang={lang}
      />
      <window.FilterBar
        t={t}
        lang={lang}
        filters={filters}
        setFilters={setFilters}
        sort={sort}
        setSort={setSort}
        count={filtered.length}
        suggestions={suggestions}
        onResetAll={resetFilters}
        activeProfile={activeProfile}
        onOpenMobileFilters={() => setMobileFiltersOpen(true)}
        dataAvailability={dataAvailability}
      />
      <window.ActiveFilterChips
        t={t}
        lang={lang}
        filters={filters}
        setFilters={setFilters}
        autocompleteValues={autocompleteValues}
        setAutocompleteValues={setAutocompleteValues}
        mapBounds={mapBounds}
        setMapBounds={setMapBounds}
        activeProfile={activeProfile}
        onResetAll={resetFilters}
      />
      <div className="main-grid" data-view={mobileView}>
        <div className="list-pane">
          <div className="list-grid">
            {isLoading ? (
              // Skeleton loading — 6-8 cartes grises animées
              <window.SkeletonCards count={6} />
            ) : filtered.length === 0 ? (
              // Empty state amélioré avec CTAs
              <window.EmptyState
                lang={lang}
                t={t}
                filters={filters}
                setFilters={setFilters}
                onResetFilters={resetFilters}
                onChangeProfile={() => setProfileModalOpen(true)}
              />
            ) : (
              filtered.map(l => (
                <window.ListingCard
                  key={l.id}
                  listing={l}
                  t={t}
                  lang={lang}
                  activeProfile={activeProfile}
                  active={hoveredId === l.id || selectedListingId === l.id || (activeListing && activeListing.id === l.id)}
                  selected={selectedListingId === l.id}
                  onHover={() => setHoveredId(l.id)}
                  onLeave={() => setHoveredId(prev => prev === l.id ? null : prev)}
                  onClick={() => openDrawer(l)}
                  isFav={favs.has(l.id)}
                  toggleFav={() => toggleFav(l.id)}
                />
              ))
            )}
          </div>
        </div>
        <window.ListingMap
          listings={filtered}
          t={t}
          lang={lang}
          activeProfile={activeProfile}
          hoveredId={hoveredId}
          activeId={activeListing && activeListing.id}
          selectedId={selectedListingId}
          setHoveredId={setHoveredId}
          setSelectedListingId={setSelectedListingId}
          onPinClick={openDrawer}
          onBoundsChange={handleBoundsChange}
          lockBounds={mapBounds !== null}
          showSearchArea={showSearchArea}
          onApplySearchArea={applySearchArea}
        />
      </div>
      <window.Drawer
        listing={activeListing}
        t={t}
        lang={lang}
        activeProfile={activeProfile}
        open={drawerOpen}
        onClose={closeDrawer}
        compareIds={compareIds}
        onToggleCompare={toggleCompare}
        userProfile={userProfile}
      />

      {/* Compare chip — visible dès qu'au moins 1 bien est sélectionné */}
      {compareIds.length > 0 && (
        <button
          onClick={() => window.navigateTo('#/compare')}
          style={{
            position: 'fixed', bottom: 72, right: 20, zIndex: 500,
            display: 'flex', alignItems: 'center', gap: 8,
            padding: '10px 18px', borderRadius: 24,
            background: 'var(--brand, #2c3e2d)', color: '#fff',
            border: 'none', cursor: 'pointer', boxShadow: '0 4px 16px rgba(0,0,0,0.22)',
            fontWeight: 600, fontSize: 14, fontFamily: 'var(--font-body)',
          }}
        >
          <span style={{
            background: 'rgba(255,255,255,0.25)', borderRadius: '50%',
            width: 22, height: 22, display: 'inline-flex', alignItems: 'center',
            justifyContent: 'center', fontSize: 12, fontWeight: 700
          }}>{compareIds.length}</span>
          {lang === 'fr' ? 'Comparer' : 'Compare'}
        </button>
      )}

      {mobileFiltersOpen && (
        <window.MobileFilterDrawer
          t={t}
          lang={lang}
          filters={filters}
          setFilters={setFilters}
          sort={sort}
          setSort={setSort}
          count={filtered.length}
          suggestions={suggestions}
          activeProfile={activeProfile}
          onResetAll={resetFilters}
          onClose={() => setMobileFiltersOpen(false)}
          dataAvailability={dataAvailability}
        />
      )}
      <window.UserProfileModal
        open={profileModalOpen}
        onClose={() => setProfileModalOpen(false)}
        lang={lang}
        onStrategyChange={(id) => changeProfile(id)}
        onSave={(profile) => setUserProfile(profile)}
      />
      <window.LoginModal
        open={loginModalOpen}
        onClose={() => setLoginModalOpen(false)}
        lang={lang}
      />
      <div className="mobile-view-toggle">
        <button className={mobileView === "list" ? "active" : ""} onClick={() => setMobileView("list")}>
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4">
            <path d="M2 3.5h10M2 7h10M2 10.5h10"/>
          </svg>
          {lang === "fr" ? "Liste" : "List"}
        </button>
        <button className={mobileView === "map" ? "active" : ""} onClick={() => setMobileView("map")}>
          <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4">
            <circle cx="7" cy="6" r="2.2"/>
            <path d="M7 10c-2-2.5-3.5-4-3.5-5.5a3.5 3.5 0 117 0C10.5 6 9 7.5 7 10z"/>
          </svg>
          {lang === "fr" ? "Carte" : "Map"}
        </button>
      </div>
    </div>
  );
}

// Validation des filtres PR #11 — à vérifier en console après chargement
window.addEventListener('DOMContentLoaded', () => {
  const ls = window.LISTINGS || [];
  if (ls.length === 0) return;
  console.log('[PR#11 filters] listings vsMarket=below:', ls.filter(l => l.marketContext?.vsMarket === 'sous le marché').length);
  console.log('[PR#11 filters] listings daysOnMarket > 30:', ls.filter(l => l.daysOnMarket && l.daysOnMarket > 30).length);
  console.log('[PR#11 filters] listings yearBuilt set:', ls.filter(l => l.yearBuilt && l.yearBuilt > 0).length);
  console.log('[PR#11 filters] listings bedrooms set:', ls.filter(l => l.bedrooms && l.bedrooms > 0).length);
  console.log('[PR#11 filters] listings bathrooms set:', ls.filter(l => l.bathrooms && l.bathrooms > 0).length);
  console.log('[PR#11 filters] listings area set (grossArea):', ls.filter(l => l.grossArea && l.grossArea > 0).length);
});

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