import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { trackPageview } from './usePlausible';
import Map from './components/Map';
import Filters from './components/Filters';
import POIPane from './components/POIPane';
import { PropertiesPane } from './components/PropertiesPane';
import AreaPane from './components/AreaPane';
import DataSources from './components/DataSources';
import DataSourcesPage from './components/DataSourcesPage';
import FAQPage from './components/FAQPage';
import HomePage from './components/HomePage';
import Header, { type Page } from './components/Header';
import type {
  FeatureMeta,
  FeatureGroup,
  FeatureFilters,
  Bounds,
  HexagonData,
  ViewChangeParams,
  ApiResponse,
  POI,
  POIResponse,
  POICategoriesResponse,
  POICategoryGroup,
  Property,
  HexagonPropertiesResponse,
  HexagonStatsResponse,
} from './types';
import { fetchWithRetry, getApiBaseUrl, buildFilterString } from './lib/api';
import { parseUrlState, DEFAULT_VIEW } from './lib/url-state';
import { useTheme } from './hooks/useTheme';
import { useUrlSync } from './hooks/useUrlSync';

declare global {
  interface Window {
    __og_ready?: boolean;
  }
}

const DEBOUNCE_MS = 150;

export default function App() {
  const urlState = useMemo(() => parseUrlState(), []);

  const isScreenshotMode = useMemo(() => {
    const params = new URLSearchParams(window.location.search);
    return params.get('screenshot') === '1';
  }, []);

  const [features, setFeatures] = useState<FeatureMeta[]>([]);
  const [filters, setFilters] = useState<FeatureFilters>(urlState.filters || {});
  const [activeFeature, setActiveFeature] = useState<string | null>(null);
  const [dragValue, setDragValue] = useState<[number, number] | null>(null);
  const [pinnedFeature, setPinnedFeature] = useState<string | null>(null);
  const [rawData, setRawData] = useState<HexagonData[]>([]);
  const [dragData, setDragData] = useState<HexagonData[] | null>(null);
  const [resolution, setResolution] = useState<number>(8);
  const [bounds, setBounds] = useState<Bounds | null>(null);
  const [loading, setLoading] = useState<boolean>(false);
  const [zoom, setZoom] = useState<number>(urlState.viewState?.zoom || DEFAULT_VIEW.zoom);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const abortControllerRef = useRef<AbortController | null>(null);
  const dragAbortRef = useRef<AbortController | null>(null);

  const [currentView, setCurrentView] = useState<{
    latitude: number;
    longitude: number;
    zoom: number;
  } | null>(
    urlState.viewState
      ? {
          latitude: urlState.viewState.latitude,
          longitude: urlState.viewState.longitude,
          zoom: urlState.viewState.zoom,
        }
      : null
  );

  const initialViewState = useMemo(() => urlState.viewState || DEFAULT_VIEW, []);

  const [pois, setPois] = useState<POI[]>([]);
  const [poiCategoryGroups, setPOICategoryGroups] = useState<POICategoryGroup[]>([]);
  const [selectedPOICategories, setSelectedPOICategories] = useState<Set<string>>(
    urlState.poiCategories || new Set()
  );
  const poiDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const poiAbortControllerRef = useRef<AbortController | null>(null);

  const [selectedHexagon, setSelectedHexagon] = useState<{ h3: string; resolution: number } | null>(
    null
  );
  const [properties, setProperties] = useState<Property[]>([]);
  const [propertiesTotal, setPropertiesTotal] = useState(0);
  const [propertiesOffset, setPropertiesOffset] = useState(0);
  const [loadingProperties, setLoadingProperties] = useState(false);
  const [rightPaneTab, setRightPaneTab] = useState<'pois' | 'properties' | 'area'>(
    urlState.tab || 'pois'
  );

  const [areaStats, setAreaStats] = useState<HexagonStatsResponse | null>(null);
  const [loadingAreaStats, setLoadingAreaStats] = useState(false);

  const [hoveredHexagon, setHoveredHexagon] = useState<string | null>(null);
  const [hoveredAreaStats, setHoveredAreaStats] = useState<HexagonStatsResponse | null>(null);
  const [hoveredProperties, setHoveredProperties] = useState<Property[] | null>(null);
  const [hoveredPropertiesTotal, setHoveredPropertiesTotal] = useState(0);
  const [loadingHoveredAreaStats, setLoadingHoveredAreaStats] = useState(false);
  const [hoverMode, setHoverMode] = useState(true);
  const hoverAbortRef = useRef<AbortController | null>(null);
  const hoverDebounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [initialLoading, setInitialLoading] = useState(true);

  const [activePage, setActivePage] = useState<Page>(() => {
    if (isScreenshotMode) return 'dashboard';
    if (window.history.state?.page) return window.history.state.page;
    const params = new URLSearchParams(window.location.search);
    return params.has('v') || params.has('f') || params.has('poi') || params.has('tab')
      ? 'dashboard'
      : 'home';
  });

  const [pendingInfoFeature, setPendingInfoFeature] = useState<string | null>(null);

  const navigateTo = useCallback((page: Page, hash?: string, infoFeature?: string) => {
    if (infoFeature) {
      window.history.replaceState({ ...window.history.state, infoFeature }, '');
    }
    const url = hash
      ? `${window.location.pathname}${window.location.search}#${hash}`
      : `${window.location.pathname}${window.location.search}`;
    window.history.pushState({ page }, '', url);
    setActivePage(page);
    trackPageview();
  }, []);

  useEffect(() => {
    if (!window.history.state?.page) {
      window.history.replaceState({ page: activePage }, '');
    }
    const handlePopState = (e: PopStateEvent) => {
      if (e.state?.page) {
        setActivePage(e.state.page);
        if (e.state.infoFeature) {
          setPendingInfoFeature(e.state.infoFeature);
        }
      }
    };
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const { theme, toggleTheme } = useTheme();

  useEffect(() => {
    if (isScreenshotMode && !initialLoading && rawData.length > 0) {
      window.__og_ready = true;
    }
  }, [isScreenshotMode, initialLoading, rawData]);

  const enabledFeatures = useMemo(() => new Set(Object.keys(filters)), [filters]);

  const viewFeature = activeFeature || pinnedFeature;
  const viewSource: 'drag' | 'eye' | null = activeFeature ? 'drag' : pinnedFeature ? 'eye' : null;

  const colorRange = useMemo((): [number, number] | null => {
    if (!viewFeature) return null;
    const meta = features.find((f) => f.name === viewFeature);
    if (!meta) return null;
    if (meta.type === 'enum' && meta.values && meta.values.length > 0) {
      return [0, meta.values.length - 1];
    }
    if (activeFeature === viewFeature && dragValue) return dragValue;
    const filterVal = filters[viewFeature];
    if (filterVal && typeof filterVal[0] === 'number') return filterVal as [number, number];
    if (meta.min != null && meta.max != null) return [meta.min, meta.max];
    return null;
  }, [viewFeature, features, activeFeature, dragValue, filters]);

  const filterRange = useMemo((): [number, number] | null => {
    if (!viewFeature) return null;
    if (activeFeature && dragValue) return dragValue;
    const filterVal = filters[viewFeature];
    if (filterVal && typeof filterVal[0] === 'number') return filterVal as [number, number];
    return null;
  }, [viewFeature, activeFeature, dragValue, filters]);

  useUrlSync(currentView, filters, features, selectedPOICategories, rightPaneTab);

  useEffect(() => {
    const controller = new AbortController();
    let featuresLoaded = false;
    let poisLoaded = false;

    const checkDone = () => {
      if (featuresLoaded && poisLoaded) setInitialLoading(false);
    };

    fetchWithRetry<{ groups: FeatureGroup[] }>(
      `${getApiBaseUrl()}/api/features`,
      (json) => {
        const flat: FeatureMeta[] = json.groups.flatMap((g) =>
          g.features.map((f) => ({ ...f, group: g.name }))
        );
        setFeatures(flat);
        featuresLoaded = true;
        checkDone();
      },
      controller.signal
    );

    fetchWithRetry<POICategoriesResponse>(
      `${getApiBaseUrl()}/api/poi-categories`,
      (json) => {
        setPOICategoryGroups(json.groups);
        poisLoaded = true;
        checkDone();
      },
      controller.signal
    );

    return () => controller.abort();
  }, []);

  const buildFilterParam = useCallback(
    (): string => buildFilterString(filters, features),
    [filters, features]
  );

  useEffect(() => {
    if (!bounds) return;

    if (debounceRef.current) {
      clearTimeout(debounceRef.current);
    }

    debounceRef.current = setTimeout(async () => {
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
      abortControllerRef.current = new AbortController();

      setLoading(true);
      try {
        const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
        const filtersStr = buildFilterParam();

        const params = new URLSearchParams({
          resolution: resolution.toString(),
          bounds: boundsStr,
        });
        if (filtersStr) params.set('filters', filtersStr);
        if (viewFeature) {
          params.set('fields', viewFeature);
        } else {
          params.set('fields', '');
        }
        const res = await fetch(`${getApiBaseUrl()}/api/hexagons?${params}`, {
          signal: abortControllerRef.current.signal,
        });
        const json: ApiResponse = await res.json();
        setRawData(json.features || []);
      } catch (err) {
        if (err instanceof Error && err.name !== 'AbortError') {
          console.error('Failed to fetch data:', err);
        }
      } finally {
        setLoading(false);
      }
    }, DEBOUNCE_MS);

    return () => {
      if (debounceRef.current) {
        clearTimeout(debounceRef.current);
      }
    };
  }, [resolution, bounds, filters, buildFilterParam, viewFeature]);

  const data = dragData ?? rawData;

  useEffect(() => {
    if (!bounds || selectedPOICategories.size === 0) {
      setPois([]);
      return;
    }

    if (poiDebounceRef.current) {
      clearTimeout(poiDebounceRef.current);
    }

    poiDebounceRef.current = setTimeout(async () => {
      if (poiAbortControllerRef.current) {
        poiAbortControllerRef.current.abort();
      }
      poiAbortControllerRef.current = new AbortController();

      try {
        const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
        const categoriesStr = Array.from(selectedPOICategories).join(',');
        const params = new URLSearchParams({
          categories: categoriesStr,
          bounds: boundsStr,
        });
        const res = await fetch(`${getApiBaseUrl()}/api/pois?${params}`, {
          signal: poiAbortControllerRef.current.signal,
        });
        const json: POIResponse = await res.json();
        setPois(json.pois || []);
      } catch (err) {
        if (err instanceof Error && err.name !== 'AbortError') {
          console.error('Failed to fetch POIs:', err);
        }
      }
    }, DEBOUNCE_MS);

    return () => {
      if (poiDebounceRef.current) {
        clearTimeout(poiDebounceRef.current);
      }
    };
  }, [bounds, selectedPOICategories]);

  const prevBoundsRef = useRef<string>('');
  const handleViewChange = useCallback(
    ({
      resolution: newRes,
      bounds: newBounds,
      zoom: newZoom,
      latitude,
      longitude,
    }: ViewChangeParams) => {
      const boundsKey = `${newBounds.south},${newBounds.west},${newBounds.north},${newBounds.east},${newRes}`;
      if (boundsKey !== prevBoundsRef.current) {
        prevBoundsRef.current = boundsKey;
        setResolution(newRes);
        setBounds(newBounds);
      }
      setZoom(newZoom);
      setCurrentView({ latitude, longitude, zoom: newZoom });
    },
    []
  );

  const handleAddFilter = useCallback(
    (name: string) => {
      const meta = features.find((f) => f.name === name);
      if (!meta) return;
      if (meta.type === 'enum' && meta.values) {
        setFilters((prev) => ({ ...prev, [name]: [...meta.values!] }));
      } else if (meta.min != null && meta.max != null) {
        setFilters((prev) => ({ ...prev, [name]: [meta.min!, meta.max!] }));
      }
    },
    [features]
  );

  const handleFilterChange = useCallback((name: string, value: [number, number] | string[]) => {
    setFilters((prev) => ({ ...prev, [name]: value }));
  }, []);

  const handleRemoveFilter = useCallback((name: string) => {
    setFilters((prev) => {
      const next = { ...prev };
      delete next[name];
      return next;
    });
    setPinnedFeature((prev) => (prev === name ? null : prev));
  }, []);

  const handleDragStart = useCallback(
    (name: string) => {
      const meta = features.find((f) => f.name === name);
      if (meta?.type === 'enum') return;
      setActiveFeature(name);
      const fval = filters[name];
      setDragValue(fval && !Array.isArray(fval[0]) ? (fval as [number, number]) : null);

      if (!bounds) return;
      if (dragAbortRef.current) dragAbortRef.current.abort();
      dragAbortRef.current = new AbortController();

      const otherFilters = Object.entries(filters).filter(([k]) => k !== name);
      let filtersStr = '';
      if (otherFilters.length > 0) {
        filtersStr = otherFilters
          .map(([n, value]) => {
            const m = features.find((f) => f.name === n);
            if (m?.type === 'enum') return `${n}:${(value as string[]).join('|')}`;
            const [min, max] = value as [number, number];
            return `${n}:${min}:${max}`;
          })
          .join(',');
      }

      const boundsStr = `${bounds.south},${bounds.west},${bounds.north},${bounds.east}`;
      const params = new URLSearchParams({ resolution: resolution.toString(), bounds: boundsStr });
      if (filtersStr) params.set('filters', filtersStr);
      params.set('fields', name);

      fetch(`${getApiBaseUrl()}/api/hexagons?${params}`, {
        signal: dragAbortRef.current.signal,
      })
        .then((res) => res.json())
        .then((json: ApiResponse) => setDragData(json.features || []))
        .catch((err) => {
          if (err instanceof Error && err.name !== 'AbortError') {
            console.error('Failed to fetch drag data:', err);
          }
        });
    },
    [filters, features, bounds, resolution]
  );

  const handleDragChange = useCallback((value: [number, number]) => {
    setDragValue(value);
  }, []);

  const handleDragEnd = useCallback(() => {
    if (activeFeature && dragValue) {
      setFilters((prev) => ({ ...prev, [activeFeature]: dragValue }));
    }
    setActiveFeature(null);
    setDragValue(null);
    setDragData(null);
    if (dragAbortRef.current) {
      dragAbortRef.current.abort();
      dragAbortRef.current = null;
    }
  }, [activeFeature, dragValue]);

  const handleTogglePin = useCallback((name: string) => {
    setPinnedFeature((prev) => (prev === name ? null : name));
  }, []);

  const handleCancelPin = useCallback(() => {
    setPinnedFeature(null);
  }, []);

  const fetchHexagonStats = useCallback(
    async (h3: string, res: number, signal?: AbortSignal, fields?: string[]) => {
      const params = new URLSearchParams({
        h3,
        resolution: res.toString(),
      });
      const filterStr = buildFilterString(filters, features);
      if (filterStr) params.append('filters', filterStr);
      if (fields) {
        params.set('fields', fields.join(','));
      }
      const response = await fetch(`${getApiBaseUrl()}/api/hexagon-stats?${params}`, { signal });
      return (await response.json()) as HexagonStatsResponse;
    },
    [filters, features]
  );

  const fetchHexagonProperties = useCallback(
    async (h3: string, res: number, offset = 0) => {
      setLoadingProperties(true);
      try {
        const params = new URLSearchParams({
          h3,
          resolution: res.toString(),
          limit: '100',
          offset: offset.toString(),
        });

        const filterStr = buildFilterString(filters, features);
        if (filterStr) params.append('filters', filterStr);

        const response = await fetch(`${getApiBaseUrl()}/api/hexagon-properties?${params}`);
        const data: HexagonPropertiesResponse = await response.json();

        if (offset === 0) {
          setProperties(data.properties);
        } else {
          setProperties((prev) => [...prev, ...data.properties]);
        }
        setPropertiesTotal(data.total);
        setPropertiesOffset(offset + data.properties.length);
      } catch (err) {
        console.error('Failed to fetch properties:', err);
      } finally {
        setLoadingProperties(false);
      }
    },
    [filters, features]
  );

  const handleHexagonClick = useCallback(
    (h3: string) => {
      if (selectedHexagon?.h3 === h3) {
        setSelectedHexagon(null);
        setProperties([]);
        setAreaStats(null);
      } else {
        setSelectedHexagon({ h3, resolution });
        setPropertiesOffset(0);
        setRightPaneTab('area');
        setLoadingAreaStats(true);
        fetchHexagonStats(h3, resolution)
          .then((stats) => setAreaStats(stats))
          .catch((error) => {
            if (error instanceof Error && error.name !== 'AbortError') {
              console.error('Failed to fetch area stats:', error);
            }
          })
          .finally(() => setLoadingAreaStats(false));
      }
    },
    [selectedHexagon, resolution, fetchHexagonStats]
  );

  const handleHexagonHover = useCallback(
    (h3: string | null) => {
      setHoveredHexagon(h3);
      if (!hoverMode || !h3 || h3 === selectedHexagon?.h3) {
        if (hoverDebounceRef.current) clearTimeout(hoverDebounceRef.current);
        if (hoverAbortRef.current) hoverAbortRef.current.abort();
        setHoveredAreaStats(null);
        setHoveredProperties(null);
        setHoveredPropertiesTotal(0);
        return;
      }

      if (hoverDebounceRef.current) clearTimeout(hoverDebounceRef.current);
      hoverDebounceRef.current = setTimeout(async () => {
        if (hoverAbortRef.current) hoverAbortRef.current.abort();
        hoverAbortRef.current = new AbortController();
        const signal = hoverAbortRef.current.signal;

        try {
          if (rightPaneTab === 'area') {
            setLoadingHoveredAreaStats(true);
            const hoverFields = Object.keys(filters);
            const stats = await fetchHexagonStats(
              h3,
              resolution,
              signal,
              hoverFields.length > 0 ? hoverFields : undefined
            );
            if (!signal.aborted) setHoveredAreaStats(stats);
          } else if (rightPaneTab === 'properties') {
            const params = new URLSearchParams({
              h3,
              resolution: resolution.toString(),
              limit: '3',
              offset: '0',
            });
            const filterStr = buildFilterString(filters, features);
            if (filterStr) params.append('filters', filterStr);
            const response = await fetch(`${getApiBaseUrl()}/api/hexagon-properties?${params}`, {
              signal,
            });
            const data: HexagonPropertiesResponse = await response.json();
            if (!signal.aborted) {
              setHoveredProperties(data.properties);
              setHoveredPropertiesTotal(data.total);
            }
          }
        } catch (error) {
          if (error instanceof Error && error.name !== 'AbortError') {
            console.error('Failed to fetch hover data:', error);
          }
        } finally {
          if (!signal.aborted) setLoadingHoveredAreaStats(false);
        }
      }, DEBOUNCE_MS);
    },
    [hoverMode, selectedHexagon, rightPaneTab, resolution, filters, features, fetchHexagonStats]
  );

  const handleViewPropertiesFromArea = useCallback(() => {
    if (selectedHexagon) {
      setRightPaneTab('properties');
      setPropertiesOffset(0);
      fetchHexagonProperties(selectedHexagon.h3, selectedHexagon.resolution, 0);
    }
  }, [selectedHexagon, fetchHexagonProperties]);

  const handleLoadMoreProperties = useCallback(() => {
    if (selectedHexagon) {
      fetchHexagonProperties(selectedHexagon.h3, selectedHexagon.resolution, propertiesOffset);
    }
  }, [selectedHexagon, propertiesOffset, fetchHexagonProperties]);

  const handleCloseProperties = useCallback(() => {
    setSelectedHexagon(null);
    setProperties([]);
    setAreaStats(null);
  }, []);

  if (isScreenshotMode) {
    return (
      <div className="h-screen w-screen">
        <Map
          data={data}
          pois={pois}
          onViewChange={handleViewChange}
          viewFeature={viewFeature}
          colorRange={colorRange}
          filterRange={filterRange}
          viewSource={viewSource}
          onCancelPin={handleCancelPin}
          features={features}
          selectedHexagonId={null}
          hoveredHexagonId={null}
          onHexagonClick={() => {}}
          onHexagonHover={() => {}}
          initialViewState={initialViewState}
          theme={theme}
          screenshotMode
        />
      </div>
    );
  }

  return (
    <div className="h-screen flex flex-col">
      <Header
        activePage={activePage}
        onPageChange={navigateTo}
        theme={theme}
        onToggleTheme={toggleTheme}
      />
      {activePage === 'home' ? (
        <HomePage onOpenDashboard={() => navigateTo('dashboard')} theme={theme} />
      ) : activePage === 'data-sources' ? (
        <DataSourcesPage />
      ) : activePage === 'faq' ? (
        <FAQPage />
      ) : (
        <div className="flex-1 flex overflow-hidden relative">
          {initialLoading && (
            <div className="absolute inset-0 z-50 flex items-center justify-center bg-warm-50/80 dark:bg-navy-950/80 backdrop-blur-sm">
              <div className="flex flex-col items-center gap-4">
                <svg
                  className="w-12 h-12 text-teal-600 dark:text-teal-400 animate-spin"
                  fill="none"
                  viewBox="0 0 24 24"
                >
                  <circle
                    className="opacity-25"
                    cx="12"
                    cy="12"
                    r="10"
                    stroke="currentColor"
                    strokeWidth="4"
                  />
                  <path
                    className="opacity-75"
                    fill="currentColor"
                    d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
                  />
                </svg>
                <p className="text-warm-600 dark:text-warm-300 text-sm font-medium">
                  Connecting to server...
                </p>
              </div>
            </div>
          )}
          <Filters
            features={features}
            filters={filters}
            activeFeature={activeFeature}
            dragValue={dragValue}
            enabledFeatures={enabledFeatures}
            onAddFilter={handleAddFilter}
            onRemoveFilter={handleRemoveFilter}
            onFilterChange={handleFilterChange}
            onDragStart={handleDragStart}
            onDragChange={handleDragChange}
            onDragEnd={handleDragEnd}
            zoom={zoom}
            pinnedFeature={pinnedFeature}
            onTogglePin={handleTogglePin}
            onCancelPin={handleCancelPin}
            onNavigateToSource={(slug, featureName) => {
              navigateTo('data-sources', slug, featureName);
            }}
            openInfoFeature={pendingInfoFeature}
            onClearOpenInfoFeature={() => setPendingInfoFeature(null)}
          />
          <div className="flex-1 relative">
            <Map
              data={data}
              pois={pois}
              onViewChange={handleViewChange}
              viewFeature={viewFeature}
              colorRange={colorRange}
              filterRange={filterRange}
              viewSource={viewSource}
              onCancelPin={handleCancelPin}
              features={features}
              selectedHexagonId={selectedHexagon?.h3 || null}
              hoveredHexagonId={hoveredHexagon}
              onHexagonClick={handleHexagonClick}
              onHexagonHover={handleHexagonHover}
              initialViewState={initialViewState}
              theme={theme}
            />
            {loading && (
              <div className="absolute bottom-4 left-4 bg-white dark:bg-navy-800 dark:text-warm-200 px-3 py-1 rounded shadow">
                Loading...
              </div>
            )}
            <DataSources onNavigate={() => navigateTo('data-sources')} />
          </div>
          <div className="w-72 bg-white dark:bg-navy-950 shadow-lg z-10 flex flex-col">
            <div className="flex border-b border-warm-200 dark:border-navy-700 text-sm">
              <button
                className={`flex-1 p-3 ${
                  rightPaneTab === 'area'
                    ? 'border-b-2 border-teal-500 font-semibold dark:text-warm-100'
                    : 'text-warm-600 dark:text-warm-400'
                }`}
                onClick={() => setRightPaneTab('area')}
              >
                Area {areaStats ? `(${areaStats.count})` : ''}
              </button>
              <button
                className={`flex-1 p-3 ${
                  rightPaneTab === 'properties'
                    ? 'border-b-2 border-teal-500 font-semibold dark:text-warm-100'
                    : 'text-warm-600 dark:text-warm-400'
                }`}
                onClick={() => setRightPaneTab('properties')}
              >
                Properties {propertiesTotal > 0 && `(${propertiesTotal})`}
              </button>
              <button
                className={`flex-1 p-3 ${
                  rightPaneTab === 'pois'
                    ? 'border-b-2 border-teal-500 font-semibold dark:text-warm-100'
                    : 'text-warm-600 dark:text-warm-400'
                }`}
                onClick={() => setRightPaneTab('pois')}
              >
                POIs {pois.length > 0 && `(${pois.length})`}
              </button>
            </div>

            <div className="flex-1 overflow-hidden">
              {rightPaneTab === 'area' ? (
                <AreaPane
                  stats={hoverMode && hoveredAreaStats ? hoveredAreaStats : areaStats}
                  globalFeatures={features}
                  loading={
                    hoverMode && hoveredHexagon && hoveredHexagon !== selectedHexagon?.h3
                      ? loadingHoveredAreaStats
                      : loadingAreaStats
                  }
                  hexagonId={
                    hoverMode && hoveredHexagon && hoveredHexagon !== selectedHexagon?.h3
                      ? hoveredHexagon
                      : selectedHexagon?.h3 || null
                  }
                  isHoveredPreview={
                    !!(
                      hoverMode &&
                      hoveredAreaStats &&
                      hoveredHexagon &&
                      hoveredHexagon !== selectedHexagon?.h3
                    )
                  }
                  hoverMode={hoverMode}
                  onHoverModeChange={setHoverMode}
                  onViewProperties={handleViewPropertiesFromArea}
                  onClose={handleCloseProperties}
                  hexagonLocation={(() => {
                    const hexId =
                      hoverMode && hoveredHexagon && hoveredHexagon !== selectedHexagon?.h3
                        ? hoveredHexagon
                        : selectedHexagon?.h3;
                    const hex = hexId ? data.find((d) => d.h3 === hexId) : null;
                    if (!hex || typeof hex.lat !== 'number' || typeof hex.lon !== 'number')
                      return null;
                    return {
                      lat: hex.lat as number,
                      lon: hex.lon as number,
                      postcode: (hex.postcode as string | undefined) ?? null,
                      resolution,
                    };
                  })()}
                  filters={filters}
                />
              ) : rightPaneTab === 'properties' ? (
                <PropertiesPane
                  properties={hoverMode && hoveredProperties ? hoveredProperties : properties}
                  total={hoverMode && hoveredProperties ? hoveredPropertiesTotal : propertiesTotal}
                  loading={loadingProperties}
                  hexagonId={
                    hoverMode && hoveredProperties ? hoveredHexagon : selectedHexagon?.h3 || null
                  }
                  onLoadMore={handleLoadMoreProperties}
                  onClose={handleCloseProperties}
                  onNavigateToSource={(slug) => navigateTo('data-sources', slug)}
                  isHoveredPreview={
                    !!(
                      hoverMode &&
                      hoveredProperties &&
                      hoveredHexagon &&
                      hoveredHexagon !== selectedHexagon?.h3
                    )
                  }
                  hoverMode={hoverMode}
                  onHoverModeChange={setHoverMode}
                />
              ) : (
                <POIPane
                  groups={poiCategoryGroups}
                  selectedCategories={selectedPOICategories}
                  onCategoriesChange={setSelectedPOICategories}
                  poiCount={pois.length}
                  onNavigateToSource={(slug) => navigateTo('data-sources', slug)}
                />
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
