import { useCallback, useRef, useEffect, useState, useMemo, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Map as MapGL, useControl, ScaleControl } from 'react-map-gl/maplibre';
import { MapboxOverlay } from '@deck.gl/mapbox';
import 'maplibre-gl/dist/maplibre-gl.css';
import type {
  HexagonData,
  PostcodeFeature,
  PostcodeGeometry,
  ViewState,
  ViewChangeParams,
  POI,
  FeatureMeta,
  Bounds,
} from '../../types';

import { zoomToResolution, getBoundsFromViewState, getMapStyle } from '../../lib/map-utils';
import {
  INITIAL_VIEW_STATE,
  MAP_MIN_ZOOM,
  MAP_BOUNDS,
  POI_GROUP_COLORS,
  POI_DEFAULT_COLOR,
} from '../../lib/consts';
import LocationSearch, { type SearchedLocation } from './LocationSearch';
import MapLegend from './MapLegend';
import HoverCard from './HoverCard';
import { LogoIcon } from '../ui/icons/LogoIcon';
import { CloseIcon } from '../ui/icons/CloseIcon';
import type { FeatureFilters } from '../../types';
import { useDeckLayers } from '../../hooks/useDeckLayers';
import { useTranslatedModes, type TravelTimeEntry } from '../../hooks/useTravelTime';

interface MapProps {
  data: HexagonData[];
  postcodeData: PostcodeFeature[];
  usePostcodeView: boolean;
  pois: POI[];
  onViewChange: (params: ViewChangeParams) => void;
  viewFeature: string | null;
  colorRange: [number, number] | 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?: React.MutableRefObject<((lat: number, lng: number, zoom: number) => void) | null>;
  theme?: 'light' | 'dark';
  screenshotMode?: boolean;
  ogMode?: boolean;
  filters?: FeatureFilters;
  selectedPostcodeGeometry?: PostcodeGeometry | null;
  onLocationSearched?: (location: SearchedLocation | null) => void;
  bounds?: Bounds | null;
  hideLegend?: boolean;
  travelTimeEntries?: TravelTimeEntry[];
  densityLabel?: string;
}

const EMPTY_TRAVEL_ENTRIES: TravelTimeEntry[] = [];

interface Dimensions {
  width: number;
  height: number;
}

function DeckOverlay({
  layers,
  getTooltip,
}: {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  layers: any[];
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  getTooltip: any;
}) {
  const overlay = useControl(() => new MapboxOverlay({ interleaved: true }));
  const prevLayersRef = useRef(layers);
  const prevTooltipRef = useRef(getTooltip);
  if (layers !== prevLayersRef.current || getTooltip !== prevTooltipRef.current) {
    prevLayersRef.current = layers;
    prevTooltipRef.current = getTooltip;
    overlay.setProps({ layers, getTooltip });
  }
  return null;
}

export default memo(function Map({
  data,
  postcodeData,
  usePostcodeView,
  pois,
  onViewChange,
  viewFeature,
  colorRange,
  filterRange,
  viewSource,
  onCancelPin,
  features,
  selectedHexagonId,
  hoveredHexagonId,
  onHexagonClick,
  onHexagonHover,
  initialViewState,
  flyToRef,
  theme = 'light',
  screenshotMode = false,
  ogMode = false,
  filters = {},
  selectedPostcodeGeometry,
  onLocationSearched,
  bounds: viewportBounds,
  hideLegend = false,
  travelTimeEntries = EMPTY_TRAVEL_ENTRIES,
  densityLabel = 'Number of properties',
}: MapProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const { t } = useTranslation();
  const modes = useTranslatedModes();
  const [internalViewState, setInternalViewState] = useState<ViewState>(
    initialViewState || INITIAL_VIEW_STATE
  );
  const [dimensions, setDimensions] = useState<Dimensions>({ width: 0, height: 0 });

  // In screenshot mode, use the prop directly for instant updates (no async lag)
  const viewState = screenshotMode && initialViewState ? initialViewState : internalViewState;

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const observer = new ResizeObserver((entries) => {
      const { width, height } = entries[0].contentRect;
      if (width > 0 && height > 0) {
        setDimensions({ width, height });
      }
    });

    observer.observe(container);
    return () => observer.disconnect();
  }, []);

  useEffect(() => {
    if (dimensions.width === 0 || dimensions.height === 0) return;

    const bounds = getBoundsFromViewState(viewState, dimensions.width, dimensions.height);
    const resolution = zoomToResolution(viewState.zoom);

    onViewChange({
      resolution,
      bounds,
      zoom: viewState.zoom,
      latitude: viewState.latitude,
      longitude: viewState.longitude,
    });
  }, [viewState, dimensions, onViewChange]);

  const handleMove = useCallback((evt: { viewState: ViewState }) => {
    setInternalViewState(evt.viewState);
  }, []);

  const handleFlyTo = useCallback((lat: number, lng: number, zoom: number) => {
    setInternalViewState((prev) => ({ ...prev, latitude: lat, longitude: lng, zoom }));
  }, []);

  if (flyToRef) flyToRef.current = handleFlyTo;

  const mapStyle = useMemo(() => getMapStyle(theme), [theme]);

  const {
    layers,
    popupInfo,
    clearPopupInfo,
    hoverPosition,
    countRange,
    postcodeCountRange,
    colorFeatureMeta,
    handleMouseLeave,
  } = useDeckLayers({
    data,
    postcodeData,
    usePostcodeView,
    zoom: viewState.zoom,
    pois,
    viewFeature,
    colorRange,
    filterRange,
    features,
    selectedHexagonId,
    hoveredHexagonId,
    onHexagonClick,
    onHexagonHover,
    theme,
    selectedPostcodeGeometry,
    bounds: viewportBounds,
    travelTimeEntries,
  });

  return (
    <div className="flex-1 h-full relative" ref={containerRef} onMouseLeave={handleMouseLeave}>
      <MapGL
        {...viewState}
        onMove={handleMove}
        onLoad={undefined}
        onIdle={screenshotMode ? () => { window.__map_idle = true; } : undefined}
        mapStyle={mapStyle}
        style={{ width: '100%', height: '100%' }}
        attributionControl={false}
        dragRotate={false}
        touchZoomRotate={true}
        touchPitch={false}
        keyboard={true}
        pitchWithRotate={false}
        minZoom={MAP_MIN_ZOOM}
        maxBounds={MAP_BOUNDS}
      >
        <DeckOverlay layers={layers} getTooltip={null} />
        {!screenshotMode && (
          <ScaleControl position="bottom-left" maxWidth={100} unit="metric" />
        )}
      </MapGL>
      {screenshotMode ? (
        ogMode ? (
          <div className="absolute inset-0 z-20 pointer-events-none flex flex-col">
            {/* Center: Logo card with hero text */}
            <div className="flex-1 flex items-center justify-center">
              <div className="flex items-center gap-8 bg-navy-900/90 rounded-3xl px-14 py-10">
                <LogoIcon className="w-24 h-24 text-teal-400" />
                <span className="font-bold text-white whitespace-nowrap" style={{ fontSize: '5rem' }}>
                  Your perfect postcode
                </span>
              </div>
            </div>

            {/* Bottom bar */}
            <div className="absolute bottom-0 left-0 right-0 flex items-center justify-between px-10 py-4 bg-white">
              <div className="flex items-center gap-6">
                <span className="text-warm-500 font-medium" style={{ fontSize: '0.95rem' }}>
                  Property prices
                </span>
                <span className="text-warm-300">|</span>
                <span className="text-warm-500 font-medium" style={{ fontSize: '0.95rem' }}>
                  Energy ratings
                </span>
                <span className="text-warm-300">|</span>
                <span className="text-warm-500 font-medium" style={{ fontSize: '0.95rem' }}>
                  Schools
                </span>
                <span className="text-warm-300">|</span>
                <span className="text-warm-500 font-medium" style={{ fontSize: '0.95rem' }}>
                  Crime stats
                </span>
                <span className="text-warm-300">|</span>
                <span className="text-warm-500 font-medium" style={{ fontSize: '0.95rem' }}>
                  Transport
                </span>
              </div>
              <span className="text-teal-600 font-semibold" style={{ fontSize: '1rem' }}>
                perfect-postcode.co.uk
              </span>
            </div>
          </div>
        ) : null
      ) : (
        <>
          <div className="absolute top-3 left-3 right-3 z-10 flex flex-wrap items-start justify-between gap-2 pointer-events-none">
            <LocationSearch
              onFlyTo={handleFlyTo}
              onLocationSearched={onLocationSearched}
              onMouseEnter={handleMouseLeave}
            />
            {!hideLegend &&
              (viewFeature && colorRange ? (
                viewFeature.startsWith('tt_') ? (
                  <MapLegend
                    featureLabel={t('travel.travelTime', { mode: modes.label(viewFeature.split('_')[1] as 'car' | 'bicycle' | 'walking' | 'transit') })}
                    range={colorRange}
                    showCancel={viewSource === 'eye'}
                    onCancel={onCancelPin}
                    mode="feature"
                    theme={theme}
                    suffix=" min"
                  />
                ) : colorFeatureMeta ? (
                  <MapLegend
                    featureLabel={
                      viewSource === 'eye'
                        ? `Previewing \u201c${colorFeatureMeta.name}\u201d`
                        : colorFeatureMeta.name
                    }
                    range={colorRange}
                    showCancel={viewSource === 'eye'}
                    onCancel={onCancelPin}
                    mode="feature"
                    enumValues={
                      colorFeatureMeta.type === 'enum' ? colorFeatureMeta.values : undefined
                    }
                    theme={theme}
                    raw={colorFeatureMeta.raw}
                  />
                ) : null
              ) : (
                <MapLegend
                  featureLabel={densityLabel}
                  range={
                    usePostcodeView
                      ? [postcodeCountRange.min, postcodeCountRange.max]
                      : [countRange.min, countRange.max]
                  }
                  totalCount={
                    usePostcodeView ? postcodeCountRange.total : countRange.total
                  }
                  showCancel={false}
                  onCancel={onCancelPin}
                  mode="density"
                  theme={theme}
                />
              ))}
          </div>
          {popupInfo && (
            <div
              className="absolute bg-white dark:bg-warm-800 rounded-lg shadow-lg text-sm dark:text-white"
              style={{
                left: popupInfo.x,
                top: popupInfo.y - 50,
                transform: 'translateX(-50%)',
                zIndex: 9999,
              }}
            >
              <button
                className="absolute -top-2 -right-2 w-5 h-5 flex items-center justify-center rounded-full bg-warm-200 dark:bg-warm-700 text-warm-500 dark:text-warm-400 hover:text-warm-700 dark:hover:text-warm-300 shadow-sm"
                onClick={clearPopupInfo}
              >
                <CloseIcon className="w-3 h-3" />
              </button>
              {popupInfo.isCluster ? (
                <div className="px-3 py-2 text-center">
                  <div className="text-lg font-bold text-teal-600 dark:text-teal-400">
                    {popupInfo.clusterCount}
                  </div>
                  <div className="text-warm-500 dark:text-warm-400 text-xs">places</div>
                </div>
              ) : (
                <div className="px-3 py-2">
                  <div className="flex items-center gap-2">
                    <span className="text-lg leading-none">{popupInfo.emoji}</span>
                    <div>
                      <div className="font-semibold dark:text-warm-100">{popupInfo.name}</div>
                      <div className="flex items-center gap-1.5 text-xs text-warm-500 dark:text-warm-400">
                        <span
                          className="inline-block w-2 h-2 rounded-full flex-shrink-0"
                          style={{
                            backgroundColor: `rgb(${(POI_GROUP_COLORS[popupInfo.group] || POI_DEFAULT_COLOR).join(',')})`,
                          }}
                        />
                        {popupInfo.category}
                      </div>
                    </div>
                  </div>
                </div>
              )}
            </div>
          )}
          {hoverPosition && hoveredHexagonId && hoveredHexagonId !== selectedHexagonId && (
            <HoverCard
              x={hoverPosition.x}
              y={hoverPosition.y}
              id={hoveredHexagonId}
              isPostcode={usePostcodeView}
              data={
                usePostcodeView
                  ? postcodeData.find((f) => f.properties.postcode === hoveredHexagonId)
                      ?.properties || null
                  : data.find((d) => d.h3 === hoveredHexagonId) || null
              }
              filters={filters}
              features={features}
            />
          )}
        </>
      )}
    </div>
  );
});
