import { Suspense, type MutableRefObject, type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';

import type {
  ActualListing,
  FeatureFilters,
  FeatureMeta,
  POI,
  PostcodeGeometry,
  ViewState,
} from '../../../types';
import type { useMapData } from '../../../hooks/useMapData';
import type { useTutorial } from '../../../hooks/useTutorial';
import type { TravelTimeEntry } from '../../../hooks/useTravelTime';
import type { getTutorialStyles } from '../../../lib/tutorial-styles';
import type { OverlayId } from '../../../lib/overlays';
import type { BasemapId } from '../../../lib/basemaps';
import type { SearchedLocation } from '../LocationSearch';
import { MapPinIcon } from '../../ui/icons/MapPinIcon';
import { EyeIcon } from '../../ui/icons/EyeIcon';
import { HouseIcon } from '../../ui/icons/HouseIcon';
import { SpinnerIcon } from '../../ui/icons/SpinnerIcon';
import { IndeterminateProgressBar } from '../../ui/IndeterminateProgressBar';
import type { MapFlyTo, PaneResizeHandlers } from './types';
import { MapFallback, PaneFallback } from './Fallbacks';
import { MapErrorBoundary } from '../MapErrorBoundary';
import { LoadingOverlay } from './LoadingOverlay';
import { Joyride, Map, MapPageSelectionPane } from './lazyComponents';

type MapData = ReturnType<typeof useMapData>;
type Tutorial = ReturnType<typeof useTutorial>;
type TutorialTheme = ReturnType<typeof getTutorialStyles>;
type RightPaneTab = 'properties' | 'area';

interface DesktopMapPageProps {
  initialLoading: boolean;
  tutorial: Tutorial;
  tutorialTheme: TutorialTheme;
  leftPaneWidth: number;
  leftPaneHandlers: PaneResizeHandlers;
  filtersPane: ReactNode;
  mapData: MapData;
  pois: POI[];
  activeOverlays: Set<OverlayId>;
  activeCrimeTypes: Set<string>;
  basemap: BasemapId;
  colorOpacity: number;
  mapViewFeature: string | null;
  filterRange: [number, number] | null;
  viewSource: 'drag' | 'eye' | null;
  onCancelPin: () => void;
  features: FeatureMeta[];
  selectedHexagonId: string | null;
  hoveredHexagonId: string | null;
  onHexagonClick: (id: string, isPostcode?: boolean, geometry?: PostcodeGeometry) => void;
  onHexagonHover: (h3: string | null, x?: number, y?: number) => void;
  initialViewState: ViewState;
  flyToRef: MutableRefObject<MapFlyTo | null>;
  theme: 'light' | 'dark';
  filters: FeatureFilters;
  selectedPostcodeGeometry: PostcodeGeometry | null;
  onLocationSearched: (location: SearchedLocation | null) => void;
  onCurrentLocationFound: (lat: number, lng: number) => void;
  currentLocation: { lat: number; lng: number } | null;
  actualListings: ActualListing[];
  actualListingsEnabled: boolean;
  actualListingsLoading: boolean;
  onToggleActualListings?: () => void;
  travelTimeEntries: TravelTimeEntry[];
  densityLabel: string;
  totalCount?: number;
  poiPaneOpen: boolean;
  onTogglePoiPane: () => void;
  poiPane: ReactNode;
  overlayPaneOpen: boolean;
  onToggleOverlayPane: () => void;
  overlayPane: ReactNode;
  showSelectionPane: boolean;
  rightPaneWidth: number;
  rightPaneHandlers: PaneResizeHandlers;
  rightPaneTab: RightPaneTab;
  onAreaTabClick: () => void;
  onPropertiesTabClick: () => void;
  onCloseSelection: () => void;
  renderAreaPane: () => ReactNode;
  renderPropertiesPane: () => ReactNode;
  toasts: ReactNode;
  upgradeModal: ReactNode;
}

export function DesktopMapPage({
  initialLoading,
  tutorial,
  tutorialTheme,
  leftPaneWidth,
  leftPaneHandlers,
  filtersPane,
  mapData,
  pois,
  activeOverlays,
  activeCrimeTypes,
  basemap,
  colorOpacity,
  mapViewFeature,
  filterRange,
  viewSource,
  onCancelPin,
  features,
  selectedHexagonId,
  hoveredHexagonId,
  onHexagonClick,
  onHexagonHover,
  initialViewState,
  flyToRef,
  theme,
  filters,
  selectedPostcodeGeometry,
  onLocationSearched,
  onCurrentLocationFound,
  currentLocation,
  actualListings,
  actualListingsEnabled,
  actualListingsLoading,
  onToggleActualListings,
  travelTimeEntries,
  densityLabel,
  totalCount,
  poiPaneOpen,
  onTogglePoiPane,
  poiPane,
  overlayPaneOpen,
  onToggleOverlayPane,
  overlayPane,
  showSelectionPane,
  rightPaneWidth,
  rightPaneHandlers,
  rightPaneTab,
  onAreaTabClick,
  onPropertiesTabClick,
  onCloseSelection,
  renderAreaPane,
  renderPropertiesPane,
  toasts,
  upgradeModal,
}: DesktopMapPageProps) {
  const { t } = useTranslation();

  return (
    <div className="flex-1 flex overflow-hidden relative">
      <LoadingOverlay show={initialLoading} />

      {tutorial.run && (
        <Suspense fallback={null}>
          <Joyride
            steps={tutorial.steps}
            run={tutorial.run}
            continuous
            onEvent={tutorial.handleCallback}
            styles={tutorialTheme.styles}
            options={{
              ...tutorialTheme.options,
              buttons: ['back', 'close', 'primary', 'skip'],
              showProgress: true,
              skipScroll: true,
            }}
            locale={{ last: t('common.finish') }}
          />
        </Suspense>
      )}

      <div
        data-tutorial="filters"
        className="relative z-40 flex bg-white dark:bg-navy-950 shadow-lg overflow-hidden"
        style={{ width: leftPaneWidth }}
      >
        <div className="flex-1 flex flex-col overflow-hidden">{filtersPane}</div>
        <div
          className="w-3 cursor-col-resize flex items-center justify-center group bg-warm-100 dark:bg-navy-800 hover:bg-warm-200 dark:hover:bg-navy-700 border-x border-warm-200 dark:border-navy-700"
          {...leftPaneHandlers}
        >
          <div className="flex flex-col gap-1.5">
            <div className="w-1 h-1 rounded-full bg-warm-300 dark:bg-navy-600 group-hover:bg-warm-400 dark:group-hover:bg-navy-500" />
            <div className="w-1 h-1 rounded-full bg-warm-300 dark:bg-navy-600 group-hover:bg-warm-400 dark:group-hover:bg-navy-500" />
            <div className="w-1 h-1 rounded-full bg-warm-300 dark:bg-navy-600 group-hover:bg-warm-400 dark:group-hover:bg-navy-500" />
          </div>
        </div>
      </div>

      <div data-tutorial="map" className="flex-1 relative">
        <IndeterminateProgressBar show={mapData.loading && !initialLoading} />
        <MapErrorBoundary>
          <Suspense fallback={<MapFallback />}>
            <Map
              data={mapData.data}
              postcodeData={mapData.postcodeData}
              usePostcodeView={mapData.usePostcodeView}
              pois={pois}
              activeOverlays={activeOverlays}
              activeCrimeTypes={activeCrimeTypes}
              basemap={basemap}
              colorOpacity={colorOpacity}
              onViewChange={mapData.handleViewChange}
              viewFeature={mapViewFeature}
              colorRange={mapData.colorRange}
              filterRange={filterRange}
              viewSource={viewSource}
              onCancelPin={onCancelPin}
              onResetPreviewScale={mapData.handleResetPreviewScale}
              canResetPreviewScale={mapData.canResetPreviewScale}
              features={features}
              selectedHexagonId={selectedHexagonId}
              hoveredHexagonId={hoveredHexagonId}
              onHexagonClick={onHexagonClick}
              onHexagonHover={onHexagonHover}
              initialViewState={initialViewState}
              flyToRef={flyToRef}
              theme={theme}
              filters={filters}
              selectedPostcodeGeometry={selectedPostcodeGeometry}
              onLocationSearched={onLocationSearched}
              onCurrentLocationFound={onCurrentLocationFound}
              currentLocation={currentLocation}
              actualListings={actualListings}
              bounds={mapData.bounds}
              hideTopCardsWhenNarrow
              travelTimeEntries={travelTimeEntries}
              densityLabel={densityLabel}
              totalCount={totalCount}
            />
          </Suspense>
        </MapErrorBoundary>
        <div className="absolute bottom-4 right-4 z-10 flex max-w-[calc(100%_-_2rem)] flex-row flex-wrap justify-end gap-2">
          {onToggleActualListings && (
            <button
              type="button"
              onClick={onToggleActualListings}
              aria-pressed={actualListingsEnabled}
              aria-busy={actualListingsLoading}
              aria-label={
                actualListingsEnabled ? t('map.actualListings.hide') : t('map.actualListings.show')
              }
              title={
                actualListingsEnabled ? t('map.actualListings.hide') : t('map.actualListings.show')
              }
              className={`flex items-center gap-2 rounded-lg bg-white px-3 py-2 shadow-lg dark:bg-warm-800 ${actualListingsEnabled ? 'text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300' : 'text-warm-500 hover:text-red-600 dark:text-warm-400 dark:hover:text-red-400'}`}
            >
              {actualListingsLoading ? (
                <SpinnerIcon className="h-5 w-5 animate-spin" />
              ) : (
                <HouseIcon className="h-5 w-5" />
              )}
              <span className="text-sm font-medium">
                {t('map.actualListings.label')}
                {actualListingsEnabled ? ` (${actualListings.length})` : ''}
              </span>
            </button>
          )}
          <button
            data-tutorial="overlays-button"
            onClick={onToggleOverlayPane}
            className={`flex items-center gap-2 rounded-lg bg-white px-3 py-2 shadow-lg dark:bg-warm-800 ${overlayPaneOpen ? 'text-teal-600 dark:text-teal-400' : 'text-warm-500 hover:text-teal-600 dark:text-warm-400 dark:hover:text-teal-400'}`}
          >
            <EyeIcon className="h-5 w-5" filled={overlayPaneOpen} />
            <span className="text-sm font-medium">{t('overlays.heading')}</span>
          </button>
          <button
            data-tutorial="poi-button"
            onClick={onTogglePoiPane}
            className={`flex items-center gap-2 rounded-lg bg-white px-3 py-2 shadow-lg dark:bg-warm-800 ${poiPaneOpen ? 'text-teal-600 dark:text-teal-400' : 'text-warm-500 hover:text-teal-600 dark:text-warm-400 dark:hover:text-teal-400'}`}
          >
            <MapPinIcon className="h-5 w-5" />
            <span className="text-sm font-medium">{t('poiPane.pointsOfInterest')}</span>
          </button>
        </div>
        {overlayPaneOpen && (
          <div className="absolute bottom-16 right-4 z-10 flex max-h-[60vh] min-h-0 w-80 flex-col overflow-hidden rounded-lg border border-warm-200 bg-white shadow-xl dark:border-warm-700 dark:bg-warm-900">
            {overlayPane}
          </div>
        )}
        {poiPaneOpen && (
          <div
            className="absolute bottom-28 right-4 z-10 flex min-h-0 w-80 max-w-[calc(100%_-_2rem)] flex-col overflow-hidden rounded-lg border border-warm-200 bg-white shadow-xl dark:border-warm-700 dark:bg-warm-900"
            style={{ height: 'min(30rem, calc(100vh - 10rem))' }}
          >
            {poiPane}
          </div>
        )}
      </div>

      {showSelectionPane && (
        <Suspense fallback={<PaneFallback />}>
          <MapPageSelectionPane
            width={rightPaneWidth}
            resizeHandlers={rightPaneHandlers}
            tab={rightPaneTab}
            onAreaTabClick={onAreaTabClick}
            onPropertiesTabClick={onPropertiesTabClick}
            onClose={onCloseSelection}
            renderAreaPane={renderAreaPane}
            renderPropertiesPane={renderPropertiesPane}
          />
        </Suspense>
      )}

      {toasts}
      {upgradeModal}
    </div>
  );
}
