import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';

import type { MapFlyToOptions, PostcodeGeometry } from '../../types';
import type { SearchedLocation } from './LocationSearch';
import { useMapData } from '../../hooks/useMapData';
import { usePOIData } from '../../hooks/usePOIData';
import { useActualListings } from '../../hooks/useActualListings';
import { buildTravelParam } from '../../lib/travel-params';
import { useFilters } from '../../hooks/useFilters';
import { useHexagonSelection } from '../../hooks/useHexagonSelection';
import { usePaneResize } from '../../hooks/usePaneResize';
import { useCollapsibleGroups } from '../../hooks/useCollapsibleGroups';
import { useAiFilters } from '../../hooks/useAiFilters';
import { useUrlSync } from '../../hooks/useUrlSync';
import { useTutorial } from '../../hooks/useTutorial';
import { getTutorialStyles } from '../../lib/tutorial-styles';
import { travelFieldKey, useTravelTime } from '../../hooks/useTravelTime';
import { apiUrl, authHeaders, buildFilterString } from '../../lib/api';
import { useFilterCounts } from '../../hooks/useFilterCounts';
import { trackEvent } from '../../lib/analytics';
import { INITIAL_VIEW_STATE, POSTCODE_SEARCH_ZOOM } from '../../lib/consts';
import { useLicense } from '../../hooks/useLicense';
import { stateToParams } from '../../lib/url-state';
import {
  AreaPane,
  Filters,
  POIPane,
  PropertiesPane,
  UpgradeModal,
} from './map-page/lazyComponents';
import { PaneFallback } from './map-page/Fallbacks';
import { DesktopMapPage } from './map-page/DesktopMapPage';
import { MobileMapPage } from './map-page/MobileMapPage';
import { ScreenshotMapPage } from './map-page/ScreenshotMapPage';
import { ExportToast } from './map-page/Toasts';
import { MobileMapLegend } from './map-page/MobileMapLegend';
import { useExportController } from './map-page/useExportController';
import {
  useHexagonLocation,
  useJourneyDestination,
  useMapViewFeature,
  useMobileDensityRange,
  useMobileLegendMeta,
} from './map-page/derivedState';
import {
  useHorizontalSwipeNavigationGuard,
  useInitialMapPageView,
  useInitialPostcodeSelection,
  useMobileBackNavigationGuard,
  useScreenshotReadySignal,
} from './map-page/effects';
import type { MapFlyTo, MapPageProps } from './map-page/types';

export type { ExportState } from './map-page/types';

type PendingFlyTo = { lat: number; lng: number; zoom: number };

export default function MapPage({
  features,
  poiCategoryGroups,
  initialFilters,
  initialViewState,
  initialPOICategories,
  initialTab,
  initialLoading,
  theme,
  pendingInfoFeature,
  onClearPendingInfoFeature,
  onNavigateTo,
  onExportStateChange,
  onDashboardParamsChange,
  screenshotMode,
  ogMode,
  isMobile = false,
  initialTravelTime,
  initialPostcode,
  shareCode,
  user,
  onLoginClick,
  onRegisterClick,
  onCheckoutLoginClick,
  onCheckoutRegisterClick,
  deferTutorial = false,
  onSaveSearch,
  savingSearch,
  editingSearch,
  onCancelEdit,
  onUpdateEdit,
  onUpdateEditInPlace,
}: MapPageProps) {
  const { t } = useTranslation();
  const [selectedPOICategories, setSelectedPOICategories] =
    useState<Set<string>>(initialPOICategories);
  const [leftPaneWidth, leftPaneHandlers] = usePaneResize(384, 200, 0.45, 'left');
  const [rightPaneWidth, rightPaneHandlers] = usePaneResize(384, 200, 0.45, 'right');
  const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
  const [mobileBottomSheetHeight, setMobileBottomSheetHeight] = useState(0);
  const [poiPaneOpen, setPoiPaneOpen] = useState(false);
  const [currentLocation, setCurrentLocation] = useState<{ lat: number; lng: number } | null>(null);

  const {
    filters,
    activeFeature,
    dragValue,
    pinnedFeature,
    enabledFeatures,
    viewFeature,
    viewSource,
    filterRange,
    handleAddFilter,
    handleFilterChange,
    handleRemoveFilter,
    handleSetFilters,
    handleDragStart,
    handleDragChange,
    handleDragEnd,
    handleDragEndNoCommit,
    handleTogglePin,
    handleCancelPin,
  } = useFilters({
    initialFilters,
    features,
  });

  const {
    fetchAiFilters,
    loading: aiFilterLoading,
    error: aiFilterError,
    errorType: aiFilterErrorType,
    notes: aiFilterNotes,
    summary: aiFilterSummary,
  } = useAiFilters();

  const {
    entries,
    activeEntries,
    handleAddEntry,
    handleRemoveEntry,
    handleSetDestination,
    handleSetEntries,
    handleTimeRangeChange,
    handleToggleBest,
  } = useTravelTime(initialTravelTime);

  const mapFlyToRef = useRef<MapFlyTo | null>(null);
  const pendingCurrentLocationFlyToRef = useRef<{ lat: number; lng: number } | null>(null);
  const pendingLocationSearchFlyToRef = useRef<PendingFlyTo | null>(null);
  const mobileDrawerPanelRectRef = useRef<DOMRectReadOnly | null>(null);
  const areaPaneScrollTopRef = useRef(0);
  const propertiesPaneScrollTopRef = useRef(0);

  const getMobileMapFlyToOptions = useCallback((): MapFlyToOptions | undefined => {
    if (!isMobile) return undefined;

    const panelRect = mobileDrawerPanelRectRef.current;
    if (mobileDrawerOpen && panelRect) {
      const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
      if (bottomInset > 0) {
        return { visibleViewportArea: { bottom: bottomInset } };
      }
    }

    return mobileBottomSheetHeight > 0
      ? { visibleArea: { bottom: mobileBottomSheetHeight } }
      : undefined;
  }, [isMobile, mobileBottomSheetHeight, mobileDrawerOpen]);

  const mapData = useMapData({
    filters,
    features,
    viewFeature,
    activeFeature,
    pinnedFeature,
    filterRange,
    travelTimeEntries: entries,
    shareCode,
  });

  const handleAiFilterSubmit = useCallback(
    async (query: string) => {
      const context = {
        filters,
        travelTime: activeEntries.map((entry) => ({
          mode: entry.mode,
          label: entry.label,
          min: entry.timeRange?.[0],
          max: entry.timeRange?.[1],
        })),
      };
      const hasContext = Object.keys(context.filters).length > 0 || context.travelTime.length > 0;

      const result = await fetchAiFilters(query, hasContext ? context : undefined);
      if (!result) return;

      handleSetFilters(result.filters);
      handleSetEntries(
        result.travelTimeFilters.map((travelTimeFilter) => ({
          mode: travelTimeFilter.mode,
          slug: travelTimeFilter.slug,
          label: travelTimeFilter.label,
          timeRange: [travelTimeFilter.min ?? 0, travelTimeFilter.max ?? 120] as [number, number],
          useBest: false,
        }))
      );

      const firstTravelTime = result.travelTimeFilters[0];
      if (!firstTravelTime?.slug) return;

      try {
        const res = await fetch(
          apiUrl('travel-destinations', new URLSearchParams({ mode: firstTravelTime.mode })),
          authHeaders({})
        );
        if (!res.ok) return;

        const data: { destinations: { slug: string; lat: number; lon: number }[] } =
          await res.json();
        const destination = data.destinations.find((item) => item.slug === firstTravelTime.slug);
        if (destination) {
          mapFlyToRef.current?.(
            destination.lat,
            destination.lon,
            mapData.currentView?.zoom ?? INITIAL_VIEW_STATE.zoom,
            getMobileMapFlyToOptions()
          );
        }
      } catch {
        // Filters are already applied; destination panning is non-critical.
      }
    },
    [
      activeEntries,
      fetchAiFilters,
      filters,
      getMobileMapFlyToOptions,
      handleSetEntries,
      handleSetFilters,
      mapData.currentView?.zoom,
    ]
  );

  const handleClearAll = useCallback(() => {
    handleSetFilters({});
    handleCancelPin();
    handleSetEntries([]);
  }, [handleSetFilters, handleCancelPin, handleSetEntries]);

  const handleTravelTimeRemoveEntry = useCallback(
    (index: number) => {
      const entry = entries[index];
      if (entry?.slug && pinnedFeature === travelFieldKey(entry)) {
        handleCancelPin();
      }
      handleRemoveEntry(index);
    },
    [handleCancelPin, handleRemoveEntry, entries, pinnedFeature]
  );

  const handleTravelTimeDragEnd = useCallback(
    (index: number) => {
      const dragEndValue = handleDragEndNoCommit();
      if (dragEndValue) handleTimeRangeChange(index, dragEndValue);
    },
    [handleDragEndNoCommit, handleTimeRangeChange]
  );

  const filterCounts = useFilterCounts(filters, features, mapData.bounds, entries, shareCode);
  const license = useLicense();

  const handleTravelTimeSetDestination = useCallback(
    (index: number, slug: string, label: string, _lat: number, _lon: number) => {
      handleSetDestination(index, slug, label);
    },
    [handleSetDestination]
  );

  const journeyDest = useJourneyDestination(entries);

  const {
    selectedHexagon,
    properties,
    propertiesTotal,
    loadingProperties,
    areaStats,
    loadingAreaStats,
    unfilteredAreaCount,
    areaStatsUseFilters,
    setAreaStatsUseFilters,
    hoveredHexagon,
    rightPaneTab,
    setRightPaneTab,
    handleHexagonClick,
    handleHexagonHover,
    handlePropertiesTabClick,
    handleLoadMoreProperties,
    handleCloseSelection,
    selectedPostcodeGeometry,
    handleLocationSearch,
    handleCurrentLocationSearch,
  } = useHexagonSelection({
    filters,
    features,
    hexagonData: mapData.committedHexagonData,
    resolution: mapData.resolution,
    usePostcodeView: mapData.usePostcodeView,
    travelTimeEntries: entries,
    shareCode,
    journeyDest,
  });

  const consumePendingLocationSearchFlyTo = useCallback((rect?: DOMRectReadOnly | null) => {
    const pending = pendingLocationSearchFlyToRef.current;
    const panelRect = rect ?? mobileDrawerPanelRectRef.current;
    if (!pending || !panelRect) return;

    const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
    const flyTo = mapFlyToRef.current;
    if (!flyTo) return;
    flyTo(pending.lat, pending.lng, pending.zoom, {
      visibleViewportArea: { bottom: bottomInset },
    });
    pendingLocationSearchFlyToRef.current = null;
  }, []);

  const handleLocationSearchResult = useCallback(
    (result: SearchedLocation | null) => {
      if (result) {
        const markerLat = result.markerLatitude;
        const markerLng = result.markerLongitude;
        if (markerLat != null && markerLng != null) {
          setCurrentLocation({ lat: markerLat, lng: markerLng });
        } else {
          setCurrentLocation(null);
        }
        handleLocationSearch(
          result.postcode,
          result.geometry,
          result.latitude,
          result.longitude,
          result.openProperties,
          result.focusAddress
        );
        if (isMobile) {
          pendingLocationSearchFlyToRef.current = {
            lat: markerLat ?? result.latitude,
            lng: markerLng ?? result.longitude,
            zoom: result.openProperties ? 17 : POSTCODE_SEARCH_ZOOM,
          };
          setMobileDrawerOpen(true);
          consumePendingLocationSearchFlyTo();
        }
      } else {
        setCurrentLocation(null);
        pendingLocationSearchFlyToRef.current = null;
        handleCloseSelection();
      }
    },
    [consumePendingLocationSearchFlyTo, handleCloseSelection, handleLocationSearch, isMobile]
  );

  const consumePendingCurrentLocationFlyTo = useCallback((rect?: DOMRectReadOnly | null) => {
    const pending = pendingCurrentLocationFlyToRef.current;
    const panelRect = rect ?? mobileDrawerPanelRectRef.current;
    if (!pending || !panelRect) return;

    const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
    const flyTo = mapFlyToRef.current;
    if (!flyTo) return;
    flyTo(pending.lat, pending.lng, 17, {
      visibleViewportArea: { bottom: bottomInset },
    });
    pendingCurrentLocationFlyToRef.current = null;
  }, []);

  const handleCurrentLocationFound = useCallback(
    (lat: number, lng: number) => {
      if (isMobile) {
        pendingCurrentLocationFlyToRef.current = { lat, lng };
        consumePendingCurrentLocationFlyTo();
      } else {
        mapFlyToRef.current?.(lat, lng, 17);
      }
      setCurrentLocation({ lat, lng });
      handleCurrentLocationSearch(lat, lng);
      if (isMobile) setMobileDrawerOpen(true);
    },
    [consumePendingCurrentLocationFlyTo, handleCurrentLocationSearch, isMobile]
  );

  const handleMobileDrawerPanelRectChange = useCallback(
    (rect: DOMRectReadOnly) => {
      mobileDrawerPanelRectRef.current = rect;
      consumePendingCurrentLocationFlyTo(rect);
      consumePendingLocationSearchFlyTo(rect);
    },
    [consumePendingCurrentLocationFlyTo, consumePendingLocationSearchFlyTo]
  );

  const handleMobileDrawerClose = useCallback(() => {
    pendingCurrentLocationFlyToRef.current = null;
    pendingLocationSearchFlyToRef.current = null;
    mobileDrawerPanelRectRef.current = null;
    setMobileDrawerOpen(false);
  }, []);

  const shareReturnViewRef = useRef(shareCode ? initialViewState : null);
  const handleZoomToFreeZone = useCallback(() => {
    const target = shareReturnViewRef.current ?? INITIAL_VIEW_STATE;
    mapFlyToRef.current?.(target.latitude, target.longitude, target.zoom);
  }, []);

  const pois = usePOIData(mapData.bounds, selectedPOICategories);
  const actualListingsFilterParam = useMemo(
    () => buildFilterString(filters, features),
    [filters, features]
  );
  const actualListingsTravelParam = useMemo(() => buildTravelParam(entries), [entries]);
  const { listings: actualListings } = useActualListings(mapData.bounds, {
    filterParam: actualListingsFilterParam,
    travelParam: actualListingsTravelParam,
  });
  const [isAreaGroupExpanded, toggleAreaGroup] = useCollapsibleGroups(true);

  useUrlSync(
    mapData.currentView,
    filters,
    features,
    selectedPOICategories,
    rightPaneTab,
    entries,
    shareCode
  );

  useInitialMapPageView(mapData, initialViewState, initialTab, setRightPaneTab);
  useInitialPostcodeSelection({
    initialPostcode,
    isMobile,
    flyTo: mapFlyToRef,
    onLocationSearch: handleLocationSearch,
    onOpenMobileDrawer: (target) => {
      pendingLocationSearchFlyToRef.current = target;
      setMobileDrawerOpen(true);
      consumePendingLocationSearchFlyTo();
    },
  });
  useHorizontalSwipeNavigationGuard();
  useMobileBackNavigationGuard(isMobile);
  useScreenshotReadySignal({
    screenshotMode,
    loading: mapData.loading,
    boundsReady: mapData.bounds != null,
    dataLength: mapData.data.length,
    postcodeDataLength: mapData.postcodeData.length,
    usePostcodeView: mapData.usePostcodeView,
    licenseRequired: mapData.licenseRequired,
  });

  const handleMobileHexagonClick = useCallback(
    (id: string, isPostcode?: boolean, geometry?: PostcodeGeometry) => {
      handleHexagonClick(id, isPostcode, geometry);
      if (id) {
        setMobileDrawerOpen(true);
      }
    },
    [handleHexagonClick]
  );

  const hexagonLocation = useHexagonLocation(
    selectedHexagon,
    mapData.postcodeData,
    mapData.resolution,
    areaStats
  );
  const tutorial = useTutorial(initialLoading, isMobile, deferTutorial || mapData.licenseRequired);
  const tutorialTheme = useMemo(() => getTutorialStyles(theme), [theme]);
  const densityLabel = t('mapLegend.historicalMatches');
  const hasActiveFilters = Object.keys(filters).length > 0 || activeEntries.length > 0;
  const mobileLegendMeta = useMobileLegendMeta(viewFeature, features);
  const mapViewFeature = useMapViewFeature(viewFeature);
  const mobileDensityRange = useMobileDensityRange(mapData);
  const { exportNotice, clearExportNotice } = useExportController({
    bounds: mapData.bounds,
    filters,
    features,
    travelTimeEntries: entries,
    shareCode,
    t,
    onExportStateChange,
  });

  const shareAndSaveView = isMobile
    ? (mapData.currentVisibleView ?? mapData.currentView)
    : mapData.currentView;
  const dashboardParams = useMemo(
    () =>
      stateToParams(
        shareAndSaveView,
        filters,
        features,
        selectedPOICategories,
        rightPaneTab,
        entries,
        shareCode
      ).toString(),
    [entries, features, filters, rightPaneTab, selectedPOICategories, shareCode, shareAndSaveView]
  );
  const handleSaveSearch = useCallback(
    async (name: string) => {
      await onSaveSearch?.(name, dashboardParams);
    },
    [dashboardParams, onSaveSearch]
  );
  const handleUpdateEditInPlaceWithParams = useCallback(async () => {
    await onUpdateEditInPlace?.(dashboardParams);
  }, [dashboardParams, onUpdateEditInPlace]);
  const checkoutReturnPath = useMemo(
    () => `/dashboard${dashboardParams ? `?${dashboardParams}` : ''}`,
    [dashboardParams]
  );

  useEffect(() => {
    onDashboardParamsChange?.(dashboardParams);
  }, [dashboardParams, onDashboardParamsChange]);

  useEffect(() => {
    if (mapData.licenseRequired) trackEvent('Upgrade Modal Shown');
  }, [mapData.licenseRequired]);

  if (screenshotMode) {
    return (
      <ScreenshotMapPage
        mapData={mapData}
        mapViewFeature={mapViewFeature}
        filterRange={filterRange}
        viewSource={viewSource}
        features={features}
        initialViewState={initialViewState}
        theme={theme}
        ogMode={ogMode}
        travelTimeEntries={entries}
      />
    );
  }

  const renderAreaPane = () => (
    <Suspense fallback={<PaneFallback />}>
      <AreaPane
        stats={areaStats}
        globalFeatures={features}
        loading={loadingAreaStats}
        hexagonId={selectedHexagon?.id || null}
        isPostcode={selectedHexagon?.type === 'postcode'}
        hexagonLocation={hexagonLocation}
        filters={filters}
        unfilteredCount={unfilteredAreaCount}
        statsUseFilters={areaStatsUseFilters}
        onStatsUseFiltersChange={setAreaStatsUseFilters}
        travelTimeEntries={activeEntries}
        shareCode={shareCode}
        isGroupExpanded={isAreaGroupExpanded}
        onToggleGroup={toggleAreaGroup}
        scrollTopRef={areaPaneScrollTopRef}
        scrollRestoreKey={selectedHexagon ? `${selectedHexagon.type}:${selectedHexagon.id}` : null}
        scrollSaveDisabled={loadingAreaStats && areaStats == null}
      />
    </Suspense>
  );

  const renderPropertiesPane = () => (
    <Suspense fallback={<PaneFallback />}>
      <PropertiesPane
        properties={properties}
        total={propertiesTotal}
        loading={loadingProperties}
        hexagonId={selectedHexagon?.id || null}
        onLoadMore={handleLoadMoreProperties}
        scrollTopRef={propertiesPaneScrollTopRef}
        scrollRestoreKey={selectedHexagon ? `${selectedHexagon.type}:${selectedHexagon.id}` : null}
        scrollSaveDisabled={loadingProperties && properties.length === 0}
      />
    </Suspense>
  );

  const renderPOIPane = () => (
    <Suspense fallback={<PaneFallback />}>
      <POIPane
        groups={poiCategoryGroups}
        selectedCategories={selectedPOICategories}
        onCategoriesChange={setSelectedPOICategories}
        poiCount={pois.length}
        onClose={() => setPoiPaneOpen(false)}
      />
    </Suspense>
  );

  const renderFilters = (options?: { destinationDropdownPortal?: boolean }) => (
    <Suspense fallback={<PaneFallback />}>
      <Filters
        features={features}
        filters={filters}
        activeFeature={activeFeature}
        dragValue={dragValue}
        enabledFeatures={enabledFeatures}
        onAddFilter={handleAddFilter}
        onRemoveFilter={handleRemoveFilter}
        onFilterChange={handleFilterChange}
        onDragStart={handleDragStart}
        onDragChange={handleDragChange}
        onDragEnd={handleDragEnd}
        pinnedFeature={pinnedFeature}
        onTogglePin={handleTogglePin}
        openInfoFeature={pendingInfoFeature}
        onClearOpenInfoFeature={onClearPendingInfoFeature}
        travelTimeEntries={entries}
        onTravelTimeAddEntry={handleAddEntry}
        onTravelTimeRemoveEntry={handleTravelTimeRemoveEntry}
        onTravelTimeSetDestination={handleTravelTimeSetDestination}
        onTravelTimeRangeChange={handleTimeRangeChange}
        onTravelTimeDragEnd={handleTravelTimeDragEnd}
        onTravelTimeToggleBest={handleToggleBest}
        aiFilterLoading={aiFilterLoading}
        aiFilterError={aiFilterError}
        aiFilterErrorType={aiFilterErrorType}
        aiFilterNotes={aiFilterNotes}
        aiFilterSummary={aiFilterSummary}
        onAiFilterSubmit={handleAiFilterSubmit}
        isLoggedIn={!!user}
        onLoginRequired={onRegisterClick}
        isLicensed={user?.subscription === 'licensed'}
        onUpgradeClick={() => onNavigateTo('pricing')}
        onResetTutorial={!isMobile ? tutorial.resetTutorial : undefined}
        filterImpacts={filterCounts.impacts}
        onClearAll={handleClearAll}
        onSaveSearch={onSaveSearch ? handleSaveSearch : undefined}
        savingSearch={savingSearch}
        editingSearchName={editingSearch?.name ?? null}
        onUpdateSearch={
          editingSearch && onUpdateEditInPlace ? handleUpdateEditInPlaceWithParams : undefined
        }
        onExitEditing={onCancelEdit}
        destinationDropdownPortal={options?.destinationDropdownPortal}
      />
    </Suspense>
  );

  const handleTogglePoiPane = () => setPoiPaneOpen((open) => !open);
  const handleMobileDrawerTabChange = (tab: 'area' | 'properties') => {
    if (tab === 'properties') {
      handlePropertiesTabClick();
    } else {
      setRightPaneTab(tab);
    }
  };

  const exportToast = (
    <ExportToast notice={exportNotice} closeLabel={t('common.close')} onClose={clearExportNotice} />
  );
  const toasts = exportToast;

  const editingBar =
    editingSearch && isMobile ? (
      <div className="flex items-center gap-2 px-3 py-2 border-b border-warm-200 dark:border-navy-700 bg-warm-50 dark:bg-navy-900">
        <span
          className="flex-1 min-w-0 truncate text-xs text-warm-700 dark:text-warm-200"
          title={editingSearch.name}
        >
          <Trans
            i18nKey="savedPage.isBeingUpdated"
            values={{ name: editingSearch.name }}
            components={{
              strong: <strong className="font-semibold text-navy-950 dark:text-warm-100" />,
            }}
          />
        </span>
        <button
          onClick={onCancelEdit}
          className="shrink-0 cursor-pointer px-2.5 py-1 rounded text-xs font-medium border border-warm-200 dark:border-warm-700 text-warm-700 dark:text-warm-200 hover:bg-warm-100 dark:hover:bg-navy-800"
        >
          {t('common.cancel')}
        </button>
        <button
          onClick={() => onUpdateEdit?.(dashboardParams)}
          disabled={savingSearch}
          className="shrink-0 cursor-pointer px-2.5 py-1 rounded text-xs font-medium bg-teal-600 text-white hover:bg-teal-700 disabled:opacity-50 disabled:cursor-wait flex items-center gap-1.5"
        >
          {savingSearch ? t('savedPage.updating') : t('common.update')}
        </button>
      </div>
    ) : null;

  const upgradeModal = mapData.licenseRequired ? (
    <Suspense fallback={null}>
      <UpgradeModal
        isLoggedIn={!!user}
        onLoginClick={() =>
          onCheckoutLoginClick ? onCheckoutLoginClick(checkoutReturnPath) : onLoginClick()
        }
        onRegisterClick={() =>
          onCheckoutRegisterClick ? onCheckoutRegisterClick(checkoutReturnPath) : onRegisterClick()
        }
        onStartCheckout={() => license.startCheckout(checkoutReturnPath)}
        onZoomToFreeZone={handleZoomToFreeZone}
        isShareReturn={!!shareReturnViewRef.current}
      />
    </Suspense>
  ) : null;

  if (isMobile) {
    return (
      <MobileMapPage
        initialLoading={initialLoading}
        mapData={mapData}
        pois={pois}
        mapViewFeature={mapViewFeature}
        filterRange={filterRange}
        viewSource={viewSource}
        onCancelPin={handleCancelPin}
        features={features}
        selectedHexagonId={selectedHexagon?.id || null}
        hoveredHexagonId={hoveredHexagon}
        onHexagonClick={handleMobileHexagonClick}
        onHexagonHover={handleHexagonHover}
        initialViewState={initialViewState}
        flyToRef={mapFlyToRef}
        theme={theme}
        filters={filters}
        selectedPostcodeGeometry={selectedPostcodeGeometry}
        onLocationSearched={handleLocationSearchResult}
        onCurrentLocationFound={handleCurrentLocationFound}
        currentLocation={currentLocation}
        actualListings={actualListings}
        travelTimeEntries={entries}
        bottomScreenInset={mobileBottomSheetHeight}
        onBottomSheetCoveredHeightChange={setMobileBottomSheetHeight}
        mobileDrawerOpen={mobileDrawerOpen}
        onMobileDrawerClose={handleMobileDrawerClose}
        onMobileDrawerPanelRectChange={handleMobileDrawerPanelRectChange}
        rightPaneTab={rightPaneTab}
        onMobileDrawerTabChange={handleMobileDrawerTabChange}
        poiPaneOpen={poiPaneOpen}
        onTogglePoiPane={handleTogglePoiPane}
        poiButtonLabel={t('poiPane.pointsOfInterest')}
        poiPane={renderPOIPane()}
        filtersPane={renderFilters({ destinationDropdownPortal: false })}
        mobileLegend={
          <MobileMapLegend
            mapViewFeature={mapViewFeature}
            colorRange={mapData.colorRange}
            viewSource={viewSource}
            mobileLegendMeta={mobileLegendMeta}
            densityLabel={densityLabel}
            densityRange={mobileDensityRange}
            theme={theme}
            canResetPreviewScale={mapData.canResetPreviewScale}
            onCancelPin={handleCancelPin}
            onResetPreviewScale={mapData.handleResetPreviewScale}
          />
        }
        renderAreaPane={renderAreaPane}
        renderPropertiesPane={renderPropertiesPane}
        toasts={toasts}
        upgradeModal={upgradeModal}
        editingBar={editingBar}
      />
    );
  }

  return (
    <DesktopMapPage
      initialLoading={initialLoading}
      tutorial={tutorial}
      tutorialTheme={tutorialTheme}
      leftPaneWidth={leftPaneWidth}
      leftPaneHandlers={leftPaneHandlers}
      filtersPane={renderFilters()}
      mapData={mapData}
      pois={pois}
      mapViewFeature={mapViewFeature}
      filterRange={filterRange}
      viewSource={viewSource}
      onCancelPin={handleCancelPin}
      features={features}
      selectedHexagonId={selectedHexagon?.id || null}
      hoveredHexagonId={hoveredHexagon}
      onHexagonClick={handleHexagonClick}
      onHexagonHover={handleHexagonHover}
      initialViewState={initialViewState}
      flyToRef={mapFlyToRef}
      theme={theme}
      filters={filters}
      selectedPostcodeGeometry={selectedPostcodeGeometry}
      onLocationSearched={handleLocationSearchResult}
      onCurrentLocationFound={handleCurrentLocationFound}
      currentLocation={currentLocation}
      actualListings={actualListings}
      travelTimeEntries={entries}
      densityLabel={densityLabel}
      totalCount={hasActiveFilters ? filterCounts.total : undefined}
      poiPaneOpen={poiPaneOpen}
      onTogglePoiPane={handleTogglePoiPane}
      poiPane={renderPOIPane()}
      showSelectionPane={!!selectedHexagon}
      rightPaneWidth={rightPaneWidth}
      rightPaneHandlers={rightPaneHandlers}
      rightPaneTab={rightPaneTab}
      onAreaTabClick={() => setRightPaneTab('area')}
      onPropertiesTabClick={handlePropertiesTabClick}
      onCloseSelection={handleCloseSelection}
      renderAreaPane={renderAreaPane}
      renderPropertiesPane={renderPropertiesPane}
      toasts={toasts}
      upgradeModal={upgradeModal}
    />
  );
}
