/* eslint-disable react-hooks/exhaustive-deps */
import clsx from "clsx";
import { Fragment, useEffect, useRef, useState } from "react";
import {
  surroundingPoints,
  categories,
  CENTER_POINT,
  type ISurroundingPoint,
} from "../../consts/surroundingPoints";
import { usePopupStore } from "../../stores/usePopupStore";
import { AnimatePresence, motion } from "framer-motion";
import LocationPopup from "../popups/LocationPopup";
import SurroundingsFilter from "../SurroundingsFilter";
import Button from "../ui/Button";
import PlusIcon from "../icons/PlusIcon";
import MinusIcon from "../icons/MinusIcon";
import FullscreenButton from "../ui/FullscreenButton";

interface Position {
  x: number;
  y: number;
}

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

interface MapProps {
  maxZoom?: number;
}

const getEventPosition = (
  e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent
): Position => {
  if ("touches" in e)
    return {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY,
    };
  return {
    x: e.clientX,
    y: e.clientY,
  };
};

const constrainPosition = (
  position: Position,
  containerSize: Size,
  imageSize: Size,
  zoom: number
): Position => {
  const scaledWidth = imageSize.width * zoom;
  const scaledHeight = imageSize.height * zoom;
  const minX = containerSize.width - scaledWidth;
  const minY = containerSize.height - scaledHeight;

  return {
    x: Math.min(0, Math.max(minX, position.x)),
    y: Math.min(0, Math.max(minY, position.y)),
  };
};

const calculateMinZoom = (containerSize: Size, imageSize: Size): number => {
  if (imageSize.width === 0 || imageSize.height === 0) return 0.1;

  const widthRatio = containerSize.width / imageSize.width;
  const heightRatio = containerSize.height / imageSize.height;

  return Math.max(widthRatio, heightRatio);
};

function SurroundingsPage({ maxZoom = 3 }: MapProps) {
  const mapRef = useRef<HTMLImageElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const containerSizeRef = useRef<Size>({ width: 0, height: 0 });
  const previousTouchDistance = useRef<number | null>(null);
  const initialTouchDistance = useRef<number | null>(null);
  const minZoomRef = useRef<number>(1);
  const markersContainerRef = useRef<HTMLDivElement>(null);

  const [originalSize, setOriginalSize] = useState<Size>({
    width: 0,
    height: 0,
  });
  const [isDragging, setIsDragging] = useState(false);
  const [startPosition, setStartPosition] = useState<Position>({ x: 0, y: 0 });
  const [position, setPosition] = useState<Position>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(0);
  const [selectedPoint, setSelectedPoint] = useState<ISurroundingPoint | null>(
    null
  );
  const [isImageLoaded, setIsImageLoaded] = useState(false);
  const [lastClickTime, setLastClickTime] = useState(0);
  const [selectedCategories, setSelectedCategories] = useState<Set<string>>(
    new Set(Array.from(categories.keys()))
  );

  const { setPopup, setParentBoundingClientRect, setSide } = usePopupStore();

  useEffect(() => {
    if (!containerRef.current || !isImageLoaded || originalSize.width === 0)
      return;

    // TODO: отцентрировать карту

    // const containerRect = containerRef.current.getBoundingClientRect();
    // const scaledWidth = originalSize.width * zoom;
    // const scaledHeight = originalSize.height * zoom;

    // const maxOffsetX = Math.max(0, scaledWidth - containerRect.width);
    // const maxOffsetY = Math.max(0, scaledHeight - containerRect.height);

    // const desiredOffsetX = containerRect.width * 0.35;
    // const desiredOffsetY = containerRect.height * 0.58;

    // const boundedOffsetX = Math.min(maxOffsetX);
    // const boundedOffsetY = Math.min(maxOffsetY);

    setPosition({
      x: 0,
      y: 0,
    });
  }, [isImageLoaded]);

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

      const { width, height } = containerRef.current.getBoundingClientRect();

      if (
        width === containerSizeRef.current.width &&
        height === containerSizeRef.current.height
      )
        return;

      containerSizeRef.current = { width, height };

      const newMinZoom = calculateMinZoom({ width, height }, originalSize);
      minZoomRef.current = newMinZoom;

      setZoom(newMinZoom);

      const scaledWidth = originalSize.width * newMinZoom;
      const scaledHeight = originalSize.height * newMinZoom;

      const maxOffsetX = Math.max(0, scaledWidth - width);
      const maxOffsetY = Math.max(0, scaledHeight - height);

      const boundedOffsetX = Math.min(maxOffsetX);
      const boundedOffsetY = Math.min(maxOffsetY);

      setPosition({
        x: -boundedOffsetX,
        y: -boundedOffsetY,
      });
    };

    updateContainerSize();

    addEventListener("resize", updateContainerSize);

    return () => {
      removeEventListener("resize", updateContainerSize);
    };
  }, [originalSize, zoom]);

  function handleLoad() {
    if (!mapRef.current || !containerRef.current) return;

    const img = new Image();
    img.src = mapRef.current.src;

    img.onload = () => {
      const newOriginalSize = {
        width: img.naturalWidth || img.width,
        height: img.naturalHeight || img.height,
      };

      setOriginalSize(newOriginalSize);

      const containerRect = containerRef.current!.getBoundingClientRect();
      const minZoom = calculateMinZoom(
        {
          width: containerRect.width,
          height: containerRect.height,
        },
        newOriginalSize
      );

      minZoomRef.current = minZoom;
      setZoom(minZoom);
      setIsImageLoaded(true);
    };
  }

  const getContainerSize = (): Size => containerSizeRef.current;

  const updatePosition = (newPosition: Position, newZoom: number = zoom) => {
    if (!containerRef.current) return;

    const containerSize = getContainerSize();
    const constrainedPosition = constrainPosition(
      newPosition,
      containerSize,
      originalSize,
      newZoom
    );

    setPosition(constrainedPosition);
  };

  const zoomToPoint = (point: Position, targetZoom: number) => {
    if (!containerRef.current) return;

    const boundedZoom = Math.min(
      maxZoom,
      Math.max(minZoomRef.current, targetZoom)
    );

    const containerRect = containerRef.current.getBoundingClientRect();
    const mouseX = point.x - containerRect.left;
    const mouseY = point.y - containerRect.top;

    const scale = boundedZoom / zoom;
    const dx = mouseX - position.x;
    const dy = mouseY - position.y;
    const newPosition = {
      x: mouseX - dx * scale,
      y: mouseY - dy * scale,
    };

    setZoom(boundedZoom);
    updatePosition(newPosition, boundedZoom);
  };

  const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => {
    if (e.touches.length === 2) {
      const touch1 = e.touches[0];
      const touch2 = e.touches[1];
      const distance = Math.hypot(
        touch1.clientX - touch2.clientX,
        touch1.clientY - touch2.clientY
      );
      initialTouchDistance.current = distance;
      previousTouchDistance.current = distance;
      return;
    }

    if (e.touches.length === 1) handleStart(e);
  };

  const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
    if (!containerRef.current) return;

    if (e.touches.length === 2) {
      e.preventDefault();
      const touch1 = e.touches[0];
      const touch2 = e.touches[1];
      const distance = Math.hypot(
        touch1.clientX - touch2.clientX,
        touch1.clientY - touch2.clientY
      );

      if (initialTouchDistance.current === null) {
        initialTouchDistance.current = distance;
        previousTouchDistance.current = distance;
        return;
      }

      if (previousTouchDistance.current === null) {
        previousTouchDistance.current = distance;
        return;
      }

      const distanceChange = Math.abs(distance - previousTouchDistance.current);
      const changePercentage =
        (distanceChange / previousTouchDistance.current) * 100;

      if (changePercentage >= 5) {
        const zoomFactor = distance > previousTouchDistance.current ? 1.1 : 0.9;
        const centerX = (touch1.clientX + touch2.clientX) / 2;
        const centerY = (touch1.clientY + touch2.clientY) / 2;

        const newZoom = Math.min(
          maxZoom,
          Math.max(minZoomRef.current, zoom * zoomFactor)
        );

        if (Math.abs(newZoom - zoom) > 0.001) {
          setZoom(newZoom);

          const containerRect = containerRef.current.getBoundingClientRect();
          const mouseX = centerX - containerRect.left;
          const mouseY = centerY - containerRect.top;

          const scale = newZoom / zoom;
          const dx = mouseX - position.x;
          const dy = mouseY - position.y;
          const newPosition = {
            x: mouseX - dx * scale,
            y: mouseY - dy * scale,
          };

          updatePosition(newPosition, newZoom);
        }

        previousTouchDistance.current = distance;
      }
      return;
    }

    if (isDragging && e.touches.length === 1) {
      const { x, y } = getEventPosition(e);
      const newPosition = {
        x: x - startPosition.x,
        y: y - startPosition.y,
      };

      updatePosition(newPosition);
    }
  };

  const handleEnd = () => setIsDragging(false);

  const handleTouchEnd = () => {
    setIsDragging(false);
    previousTouchDistance.current = null;
    initialTouchDistance.current = null;
  };

  const handleWheel = (e: WheelEvent) => {
    setPopup(null);
    // Проверяем, находится ли событие внутри scrollable контейнера
    const target = e.target as HTMLElement;
    let element: HTMLElement | null = target;

    while (element && element !== containerRef.current) {
      const style = window.getComputedStyle(element);
      const overflowY = style.overflowY || style.overflow;

      // Если элемент имеет overflow: auto или scroll и может прокручиваться
      if (
        (overflowY === "auto" || overflowY === "scroll") &&
        element.scrollHeight > element.clientHeight
      ) {
        // Проверяем, может ли элемент прокрутиться в направлении события
        const canScrollUp = element.scrollTop > 0;
        const canScrollDown =
          element.scrollTop < element.scrollHeight - element.clientHeight;

        if ((e.deltaY < 0 && canScrollUp) || (e.deltaY > 0 && canScrollDown)) {
          // Позволяем элементу прокручиваться
          return;
        }
      }

      element = element.parentElement;
    }

    e.preventDefault();

    if (!containerRef.current || !mapRef.current) return;

    const containerRect = containerRef.current.getBoundingClientRect();
    const mouseX = e.clientX - containerRect.left;
    const mouseY = e.clientY - containerRect.top;

    const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
    const newZoom = Math.min(
      maxZoom,
      Math.max(minZoomRef.current, zoom * zoomFactor)
    );

    if (Math.abs(newZoom - zoom) < 0.01) return;

    const scale = newZoom / zoom;
    const dx = mouseX - position.x;
    const dy = mouseY - position.y;
    const newPosition = {
      x: mouseX - dx * scale,
      y: mouseY - dy * scale,
    };

    if (isDragging) {
      const eventPosition = getEventPosition(e);
      setStartPosition({
        x: eventPosition.x - newPosition.x,
        y: eventPosition.y - newPosition.y,
      });
    }

    setZoom(newZoom);
    updatePosition(newPosition, newZoom);
  };

  const handleMouseMove = (
    e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>
  ) => {
    if (!isDragging || !containerRef.current) return;

    const { x, y } = getEventPosition(e);
    const newPosition = {
      x: x - startPosition.x,
      y: y - startPosition.y,
    };

    updatePosition(newPosition);
  };

  const handleStart = (
    e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>
  ) => {
    if (!mapRef.current) return;

    setIsDragging(true);
    const { x, y } = getEventPosition(e);
    setStartPosition({
      x: x - position.x,
      y: y - position.y,
    });
  };

  function smoothZoomTo(targetZoom: number, point: Position) {
    const duration = 400;
    const startZoom = zoom;
    const startTime = performance.now();

    function animateZoom(now: number) {
      const elapsed = now - startTime;
      const t = Math.min(elapsed / duration, 1);
      const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      const currentZoom = startZoom + (targetZoom - startZoom) * ease;

      zoomToPoint(point, currentZoom);

      if (t < 1) {
        requestAnimationFrame(animateZoom);
      } else {
        setZoom(targetZoom);
        zoomToPoint(point, targetZoom);
      }
    }

    requestAnimationFrame(animateZoom);
  }

  // Вычисляет границы SVG пути
  const getPathBounds = (
    pathData: string
  ): { minX: number; minY: number; maxX: number; maxY: number } | null => {
    try {
      // Создаем временный SVG элемент для получения границ пути
      const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("viewBox", "0 0 4096 2176");
      svg.style.position = "absolute";
      svg.style.visibility = "hidden";
      svg.style.width = "0";
      svg.style.height = "0";

      const path = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "path"
      );
      path.setAttribute("d", pathData);
      svg.appendChild(path);
      document.body.appendChild(svg);

      const bbox = path.getBBox();
      document.body.removeChild(svg);

      return {
        minX: bbox.x,
        minY: bbox.y,
        maxX: bbox.x + bbox.width,
        maxY: bbox.y + bbox.height,
      };
    } catch {
      return null;
    }
  };

  // Получает размеры фильтра относительно контейнера
  const getFilterBounds = (): {
    width: number;
    height: number;
    left: number;
    top: number;
  } | null => {
    if (!containerRef.current) return null;

    // Ищем фильтр по характерным классам - ищем элемент с классом, содержащим "top-4" и "left-4"
    const allElements = containerRef.current.querySelectorAll("*");
    let filterElement: HTMLElement | null = null;

    for (const el of allElements) {
      const htmlEl = el as HTMLElement;
      const className = htmlEl.className || "";
      if (
        typeof className === "string" &&
        className.includes("absolute") &&
        (className.includes("top-4") || className.includes("top-[1rem]")) &&
        (className.includes("left-4") || className.includes("left-[1rem]"))
      ) {
        filterElement = htmlEl;
        break;
      }
    }

    if (!filterElement) return null;

    const containerRect = containerRef.current.getBoundingClientRect();
    const filterRect = filterElement.getBoundingClientRect();

    return {
      width: filterRect.width,
      height: filterRect.height,
      left: filterRect.left - containerRect.left,
      top: filterRect.top - containerRect.top,
    };
  };

  // Проверяет, помещается ли область на экране при заданном зуме без перекрытия фильтром
  const isAreaFitting = (
    minX: number,
    minY: number,
    maxX: number,
    maxY: number,
    centerX: number,
    centerY: number,
    testZoom: number,
    containerSize: Size,
    filterBounds: {
      width: number;
      height: number;
      left: number;
      top: number;
    } | null,
    filterPadding: number
  ): boolean => {
    const centerScreenX = containerSize.width / 2;
    const centerScreenY = containerSize.height / 2;
    const centerMapX = centerScreenX - centerX * testZoom;
    const centerMapY = centerScreenY - centerY * testZoom;

    // Вычисляем границы области на экране
    const areaScreenLeft = centerMapX + minX * testZoom;
    const areaScreenTop = centerMapY + minY * testZoom;
    const areaScreenRight = centerMapX + maxX * testZoom;
    const areaScreenBottom = centerMapY + maxY * testZoom;

    // Проверяем, помещается ли область на экране
    if (
      areaScreenLeft < 0 ||
      areaScreenTop < 0 ||
      areaScreenRight > containerSize.width ||
      areaScreenBottom > containerSize.height
    ) {
      return false;
    }

    // Проверяем, не перекрывается ли область фильтром
    if (filterBounds) {
      const filterRight =
        filterBounds.left + filterBounds.width + filterPadding;
      const filterBottom =
        filterBounds.top + filterBounds.height + filterPadding;

      // Проверяем, не перекрывается ли область фильтром
      if (areaScreenLeft < filterRight && areaScreenTop < filterBottom) {
        return false;
      }

      // Проверяем, не перекрывается ли центральная локация фильтром
      const centerPointScreenX = centerMapX + CENTER_POINT.x * testZoom;
      const centerPointScreenY = centerMapY + CENTER_POINT.y * testZoom;
      const centerPointSize = 30;

      if (
        centerPointScreenX < filterRight &&
        centerPointScreenY < filterBottom &&
        centerPointScreenX + centerPointSize > filterBounds.left &&
        centerPointScreenY + centerPointSize > filterBounds.top
      ) {
        return false;
      }
    }

    return true;
  };

  // Вычисляет оптимальный зум для отображения области с точками и путем
  const calculateOptimalZoom = (
    point: ISurroundingPoint,
    containerSize: Size
  ): number => {
    // Находим границы области, включающей обе точки
    let minX = Math.min(point.coordinates.x, CENTER_POINT.x);
    let minY = Math.min(point.coordinates.y, CENTER_POINT.y);
    let maxX = Math.max(point.coordinates.x, CENTER_POINT.x);
    let maxY = Math.max(point.coordinates.y, CENTER_POINT.y);

    // Если есть путь, учитываем его границы
    if (point.path) {
      const paths = Array.isArray(point.path) ? point.path : [point.path];

      for (const pathData of paths) {
        const bounds = getPathBounds(pathData);
        if (bounds) {
          minX = Math.min(minX, bounds.minX);
          minY = Math.min(minY, bounds.minY);
          maxX = Math.max(maxX, bounds.maxX);
          maxY = Math.max(maxY, bounds.maxY);
        }
      }
    }

    // Получаем размеры фильтра
    const filterBounds = getFilterBounds();
    const padding = 0.2; // Базовый отступ (20% с каждой стороны)
    const filterPadding = 20; // Отступ от фильтра в пикселях

    // Вычисляем размеры области на карте
    const areaWidth = maxX - minX;
    const areaHeight = maxY - minY;
    const paddedWidth = areaWidth * (1 + padding * 2);
    const paddedHeight = areaHeight * (1 + padding * 2);

    // Вычисляем центр области на карте
    const centerX = (minX + maxX) / 2;
    const centerY = (minY + maxY) / 2;

    // Начальный зум без учета фильтра
    const baseZoom = Math.min(
      containerSize.width / paddedWidth,
      containerSize.height / paddedHeight
    );

    // Используем бинарный поиск для нахождения максимального зума
    let minZoom = minZoomRef.current;
    let maxZoomValue = Math.min(maxZoom, baseZoom * 2); // Увеличиваем верхнюю границу для поиска
    let optimalZoom = baseZoom;

    // Бинарный поиск максимального зума
    for (let i = 0; i < 20; i++) {
      const testZoom = (minZoom + maxZoomValue) / 2;

      if (
        isAreaFitting(
          minX,
          minY,
          maxX,
          maxY,
          centerX,
          centerY,
          testZoom,
          containerSize,
          filterBounds,
          filterPadding
        )
      ) {
        optimalZoom = testZoom;
        minZoom = testZoom;
      } else {
        maxZoomValue = testZoom;
      }

      // Если разница между границами очень мала, прекращаем поиск
      if (maxZoomValue - minZoom < 0.01) {
        break;
      }
    }

    // Ограничиваем зум между minZoom и maxZoom
    return Math.min(maxZoom, Math.max(minZoomRef.current, optimalZoom));
  };

  function smoothZoomToCenterPoint(point: ISurroundingPoint) {
    if (!containerRef.current) return;

    const duration = 600;
    const startZoom = zoom;
    const startPosition = { ...position };
    const startTime = performance.now();

    // Вычисляем оптимальный зум для отображения области с точками и путем
    const targetZoom = calculateOptimalZoom(point, containerSizeRef.current);

    // Вычисляем центр области (между точками и путем)
    let minX = Math.min(point.coordinates.x, CENTER_POINT.x);
    let minY = Math.min(point.coordinates.y, CENTER_POINT.y);
    let maxX = Math.max(point.coordinates.x, CENTER_POINT.x);
    let maxY = Math.max(point.coordinates.y, CENTER_POINT.y);

    // Если есть путь, учитываем его границы
    if (point.path) {
      const paths = Array.isArray(point.path) ? point.path : [point.path];

      for (const pathData of paths) {
        const bounds = getPathBounds(pathData);
        if (bounds) {
          minX = Math.min(minX, bounds.minX);
          minY = Math.min(minY, bounds.minY);
          maxX = Math.max(maxX, bounds.maxX);
          maxY = Math.max(maxY, bounds.maxY);
        }
      }
    }

    const centerX = (minX + maxX) / 2;
    const centerY = (minY + maxY) / 2;

    // Вычисляем целевую позицию, чтобы центр был по середине экрана
    const containerRect = containerRef.current.getBoundingClientRect();
    const centerScreenX = containerRect.width / 2;
    const centerScreenY = containerRect.height / 2;

    // Целевая позиция карты, чтобы центр был по середине экрана
    let targetPosition = {
      x: centerScreenX - centerX * targetZoom,
      y: centerScreenY - centerY * targetZoom,
    };

    // Проверяем, не попадает ли центральная локация в область фильтра
    const filterBounds = getFilterBounds();
    const filterPadding = 20; // Отступ от фильтра в пикселях
    const centerPointSize = 30; // Примерный размер центральной локации с отступом

    if (filterBounds) {
      // Вычисляем, где будет центральная локация на экране
      const centerPointScreenX = targetPosition.x + CENTER_POINT.x * targetZoom;
      const centerPointScreenY = targetPosition.y + CENTER_POINT.y * targetZoom;

      // Проверяем, попадает ли центральная локация в область фильтра
      const filterRight =
        filterBounds.left + filterBounds.width + filterPadding;
      const filterBottom =
        filterBounds.top + filterBounds.height + filterPadding;

      // Если центральная локация попадает в область фильтра, корректируем позицию
      if (
        centerPointScreenX < filterRight &&
        centerPointScreenY < filterBottom
      ) {
        // Вычисляем, на сколько нужно сдвинуть карту, чтобы центральная локация была справа/снизу от фильтра
        const neededShiftX = Math.max(
          0,
          filterRight - centerPointScreenX + centerPointSize
        );
        const neededShiftY = Math.max(
          0,
          filterBottom - centerPointScreenY + centerPointSize
        );

        // Сдвигаем позицию карты
        targetPosition = {
          x: targetPosition.x + neededShiftX,
          y: targetPosition.y + neededShiftY,
        };
      }
    }

    // Ограничиваем позицию
    const constrainedTargetPosition = constrainPosition(
      targetPosition,
      containerSizeRef.current,
      originalSize,
      targetZoom
    );

    function animateZoom(now: number) {
      const elapsed = now - startTime;
      const t = Math.min(elapsed / duration, 1);
      // Используем easing функцию для плавности
      const ease = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;

      const currentZoom = startZoom + (targetZoom - startZoom) * ease;
      const currentPosition = {
        x:
          startPosition.x +
          (constrainedTargetPosition.x - startPosition.x) * ease,
        y:
          startPosition.y +
          (constrainedTargetPosition.y - startPosition.y) * ease,
      };

      // Ограничиваем текущую позицию
      const constrainedPosition = constrainPosition(
        currentPosition,
        containerSizeRef.current,
        originalSize,
        currentZoom
      );

      setZoom(currentZoom);
      setPosition(constrainedPosition);

      if (t < 1) {
        requestAnimationFrame(animateZoom);
      } else {
        setZoom(targetZoom);
        setPosition(constrainedTargetPosition);
      }
    }

    requestAnimationFrame(animateZoom);
  }

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

    const containerRect = containerRef.current.getBoundingClientRect();
    const centerX = containerRect.left + containerRect.width / 2;
    const centerY = containerRect.top + containerRect.height / 2;

    const currentZoom = zoom;
    const zoomStep = 0.5;
    const targetZoom = Math.min(
      maxZoom,
      Math.max(minZoomRef.current, currentZoom + zoomStep)
    );

    // Если зум уже на максимуме, не делаем ничего
    if (Math.abs(targetZoom - currentZoom) < 0.01) return;

    smoothZoomTo(targetZoom, { x: centerX, y: centerY });
  };

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

    const containerRect = containerRef.current.getBoundingClientRect();
    const centerX = containerRect.left + containerRect.width / 2;
    const centerY = containerRect.top + containerRect.height / 2;

    const currentZoom = zoom;
    const zoomStep = 0.5;
    const targetZoom = Math.min(
      maxZoom,
      Math.max(minZoomRef.current, currentZoom - zoomStep)
    );

    // Если зум уже на минимуме, не делаем ничего
    if (Math.abs(targetZoom - currentZoom) < 0.01) return;

    smoothZoomTo(targetZoom, { x: centerX, y: centerY });
  };

  const handleClick = (
    e: React.MouseEvent<HTMLElement> | React.TouchEvent<HTMLElement>
  ) => {
    const currentTime = Date.now();

    if (
      (e.target as HTMLElement).closest("button") ||
      (e.target as HTMLElement).closest("#selected-complex-card")
    )
      return;

    if (currentTime - lastClickTime < 200) {
      const targetZoom =
        Math.abs(zoom - maxZoom) < 0.01 ? minZoomRef.current : maxZoom;
      const point = getEventPosition(e);

      smoothZoomTo(targetZoom, point);

      setLastClickTime(0);
    } else {
      setLastClickTime(currentTime);
    }
  };

  useEffect(() => {
    containerRef.current?.addEventListener("wheel", handleWheel, {
      passive: false,
    });

    return () =>
      containerRef.current?.removeEventListener("wheel", handleWheel);
  }, [isDragging, position]);

  // Устанавливаем stroke-dasharray для пунктирных линий после рендера
  useEffect(() => {
    if (!selectedPoint || !Array.isArray(selectedPoint.path)) return;

    // Находим все пунктирные пути для выбранной точки
    const svg = document.querySelector('svg[viewBox="0 0 4096 2176"]');
    if (!svg) return;

    const dashedPaths = svg.querySelectorAll(
      `path[data-dashed="true"][data-point="${selectedPoint.title}"][data-index="1"]`
    );
    dashedPaths.forEach((path) => {
      const element = path as SVGPathElement;
      element.setAttribute("stroke-dasharray", "6 6");
    });
  }, [selectedPoint]);

  const handlePointMouseEnter = (
    e: React.MouseEvent<HTMLDivElement>,
    point: ISurroundingPoint
  ) => {
    if (!containerRef.current) return;

    const rect = e.currentTarget.getBoundingClientRect();

    setParentBoundingClientRect(rect);
    setSide("top");
    setPopup(
      <LocationPopup
        title={point.title}
        travelTime={point.travelTime}
        style={{
          transform: `translateY(-${rect.width / 2}px)`,
        }}
      />
    );
  };

  const handlePointMouseLeave = () => {
    setPopup(null);
  };

  const handleToggleCategory = (category: string) => {
    if (selectedCategories.has(category)) selectedCategories.delete(category);
    else selectedCategories.add(category);

    setSelectedCategories(new Set(selectedCategories));
  };

  const filteredPoints = surroundingPoints.filter((point) =>
    selectedCategories.has(point.category)
  );

  // Сбрасываем выбранный поинт, если его категория отключена
  useEffect(() => {
    if (selectedPoint && !selectedCategories.has(selectedPoint.category)) {
      setSelectedPoint(null);
    }
  }, [selectedCategories, selectedPoint]);

  const handlePointClick = (
    e: React.MouseEvent<HTMLDivElement>,
    point: ISurroundingPoint
  ) => {
    e.stopPropagation();
    // Если кликнули по уже выбранной точке - снимаем выделение
    if (selectedPoint?.title === point.title) {
      setSelectedPoint(null);
    } else {
      setSelectedPoint(point);
      // Плавно зумим и центрируем точку и центральную локацию
      smoothZoomToCenterPoint(point);
    }
  };

  const imageStyle = {
    transform: `translateX(${position.x}px) translateY(${position.y}px) translateZ(0px) scale(${zoom})`,
    transformOrigin: "0 0",
    ...originalSize,
  };

  return (
    <div
      ref={containerRef}
      className={clsx(
        "touch-none overflow-hidden relative h-full select-none",
        isDragging ? "cursor-grabbing" : "cursor-grab"
      )}
      onTouchStart={handleTouchStart}
      onTouchEnd={handleTouchEnd}
      onTouchCancel={handleTouchEnd}
      onTouchMove={handleTouchMove}
      onMouseDown={handleStart}
      onMouseUp={handleEnd}
      onMouseLeave={() => {
        handleEnd();
        handlePointMouseLeave();
      }}
      onMouseMove={handleMouseMove}
      onClick={handleClick}
    >
      {containerRef.current?.clientWidth && (
        <img
          ref={mapRef}
          src="/img/surroundings/map.png"
          style={imageStyle}
          className="absolute max-w-none pointer-events-none"
          alt="map"
          onLoad={handleLoad}
        />
      )}
      <SurroundingsFilter
        selectedLocation={selectedPoint?.title}
        onSelectLocation={(location) => {
          const point =
            surroundingPoints.find((point) => point.title === location) || null;
          setSelectedPoint(point);
          // Плавно зумим и центрируем точку и центральную локацию при выборе через фильтр
          if (point) {
            smoothZoomToCenterPoint(point);
          }
        }}
        selectedCategories={selectedCategories}
        onToggleCategory={handleToggleCategory}
        filteredPoints={filteredPoints}
      />

      <svg
        className="absolute pointer-events-none"
        style={imageStyle}
        viewBox="0 0 4096 2176"
      >
        <AnimatePresence mode="wait">
          {selectedPoint &&
            (Array.isArray(selectedPoint.path) ? (
              // Для составных путей: сначала пунктир (index 1), потом сплошная (index 0)
              selectedPoint.path.map((pathData, index) => {
                const isDashed = index === 1; // Второй путь - пунктирный (короткий)
                const delay = isDashed ? 0 : 0.3; // Сплошная линия начинается после появления пунктира (0.3s фейд + 0.2s пауза)

                // Transition для animate (с delay)
                const animateTransition = {
                  pathLength: {
                    duration: 0.7,
                    ease: "easeInOut" as const,
                    delay,
                  },
                  pathOffset: {
                    duration: 0.7,
                    ease: "easeInOut" as const,
                    delay,
                  },
                  opacity: { duration: 0.3, delay },
                };

                // Transition для exit (без delay) - одинаковый для всех линий
                const exitTransition = {
                  pathLength: {
                    duration: 0.7,
                    ease: "easeInOut" as const,
                  },
                  pathOffset: {
                    duration: 0.7,
                    ease: "easeInOut" as const,
                  },
                  opacity: { duration: 0.3 },
                };

                // Variants для пунктирной линии с transition
                const dashedVariants = {
                  hidden: {
                    opacity: 0,
                    transition: {
                      opacity: {
                        duration: 0.3,
                        ease: "easeInOut" as const,
                      },
                    },
                  },
                  visible: {
                    opacity: 1,
                    transition: {
                      opacity: {
                        duration: 0.3,
                        delay,
                        ease: "easeInOut" as const,
                      },
                    },
                  },
                  exit: {
                    opacity: 0,
                    transition: {
                      opacity: {
                        duration: 0.3,
                        ease: "easeInOut" as const,
                      },
                    },
                  },
                };

                // Variants для сплошной линии с transition
                const solidVariants = {
                  hidden: {
                    pathLength: 0,
                    pathOffset: 1,
                    opacity: 0,
                    transition: exitTransition,
                  },
                  visible: {
                    pathLength: 1,
                    pathOffset: 0,
                    opacity: 1,
                    transition: animateTransition,
                  },
                  exit: {
                    pathLength: 0,
                    pathOffset: 1,
                    opacity: 0,
                    transition: exitTransition,
                  },
                };

                return (
                  <Fragment key={`route-${selectedPoint.title}-${index}`}>
                    {isDashed ? (
                      // Для пунктирной линии используем обычный path с JavaScript анимацией
                      <>
                        <motion.path
                          key={`route-${selectedPoint.title}-${index}-white`}
                          d={pathData}
                          fill="none"
                          stroke="#FFFFFF"
                          strokeWidth="clamp(5px, 0.347vw, 6px)"
                          strokeLinecap="round"
                          strokeDasharray="6 6"
                          variants={dashedVariants}
                          initial="hidden"
                          animate="visible"
                          exit="exit"
                        />
                        <motion.path
                          key={`route-${selectedPoint.title}-${index}-color`}
                          d={pathData}
                          fill="none"
                          strokeWidth="clamp(3px, 0.208vw, 4px)"
                          stroke="#F47F52"
                          strokeLinecap="round"
                          strokeDasharray="6 6"
                          variants={dashedVariants}
                          initial="hidden"
                          animate="visible"
                          exit="exit"
                        />
                      </>
                    ) : (
                      // Для сплошной линии используем pathOffset для анимации от точки к центру
                      <>
                        <motion.path
                          key={`route-${selectedPoint.title}-${index}-white`}
                          d={pathData}
                          fill="none"
                          stroke="#FFFFFF"
                          strokeWidth="clamp(5px, 0.347vw, 6px)"
                          strokeLinecap="round"
                          variants={solidVariants}
                          initial="hidden"
                          animate="visible"
                          exit="exit"
                        />
                        <motion.path
                          key={`route-${selectedPoint.title}-${index}-color`}
                          d={pathData}
                          fill="none"
                          stroke="#F47F52"
                          strokeWidth="clamp(3px, 0.208vw, 4px)"
                          strokeLinecap="round"
                          variants={solidVariants}
                          initial="hidden"
                          animate="visible"
                          exit="exit"
                        />
                      </>
                    )}
                  </Fragment>
                );
              })
            ) : (
              // Для простых путей: анимация от точки к центру
              <>
                <motion.path
                  key={`route-${selectedPoint.title}-white`}
                  d={selectedPoint.path}
                  fill="none"
                  stroke="#FFFFFF"
                  strokeWidth="clamp(5px, 0.347vw, 6px)"
                  strokeLinecap="round"
                  initial={{ pathLength: 0, pathOffset: 1, opacity: 0 }}
                  animate={{ pathLength: 1, pathOffset: 0, opacity: 1 }}
                  exit={{ pathLength: 0, pathOffset: 1, opacity: 0 }}
                  transition={{
                    pathOffset: { duration: 0.7, ease: "easeInOut" },
                    opacity: { duration: 0.3 },
                  }}
                />
                <motion.path
                  key={`route-${selectedPoint.title}-color`}
                  d={selectedPoint.path}
                  fill="none"
                  stroke="#F47F52"
                  strokeWidth="clamp(3px, 0.208vw, 4px)"
                  strokeLinecap="round"
                  initial={{ pathLength: 0, pathOffset: 1, opacity: 0 }}
                  animate={{ pathLength: 1, pathOffset: 0, opacity: 1 }}
                  exit={{ pathLength: 0, pathOffset: 1, opacity: 0 }}
                  transition={{
                    pathOffset: { duration: 0.7, ease: "easeInOut" },
                    opacity: { duration: 0.3 },
                  }}
                />
              </>
            ))}
        </AnimatePresence>
      </svg>

      <div className="absolute" ref={markersContainerRef} style={imageStyle}>
        <div className="relative" style={originalSize}>
          <div
            className="absolute transition-alla origin-center"
            ref={(el) => {
              if (!el) return;
              el.style.transform = `scale(${Math.min(
                1 / zoom,
                1
              )}) translate(-${(el.clientWidth * zoom) / 2}px, -${
                (el.clientHeight * zoom) / 2
              }px)`;
            }}
            style={{
              left: CENTER_POINT.x,
              top: CENTER_POINT.y,
            }}
          >
            <div className="2xl:p-[0.37vw] p-[5.33px] bg-[#F47F52] 2xl:rounded-[0.556vw] rounded-lg shadow-[0px_4px_40px_0px_rgba(244,127,82,0.3),0px_2px_2px_0px_rgba(244,127,82,0.25)]">
              <img
                ref={(el) => {
                  if (!el) return;
                  el.style.maxWidth =
                    innerWidth >= 1440
                      ? `${(el.naturalWidth / 3 / 1440) * 100}vw`
                      : `${el.naturalWidth / 3}px`;
                }}
                src="/img/surroundings/location.png"
                className="select-none"
                draggable={false}
              />
            </div>
          </div>
          <AnimatePresence>
            {filteredPoints.map((point) => (
              <motion.div
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                key={point.title}
                className="absolute"
                style={{
                  left: point.coordinates.x,
                  top: point.coordinates.y,
                }}
              >
                <div
                  onMouseEnter={(e) => handlePointMouseEnter(e, point)}
                  onMouseLeave={handlePointMouseLeave}
                  onClick={(e) => handlePointClick(e, point)}
                  ref={(el) => {
                    if (!el) return;
                    el.style.transform = `scale(${Math.min(
                      1 / zoom,
                      1
                    )}) translate(-${(el.clientWidth * zoom) / 2}px, -${
                      (el.clientHeight * zoom) / 2
                    }px)`;
                  }}
                  className="2xl:size-[1.111vw] size-4 aspect-square rounded-full 2xl:ring-[0.069vw] ring-1 ring-white cursor-pointer transition-colors"
                  style={{
                    backgroundColor:
                      selectedPoint?.title === point.title
                        ? "#F47F52"
                        : categories.get(point.category) || "#F47F52",
                  }}
                />
              </motion.div>
            ))}
          </AnimatePresence>
        </div>
      </div>
      <div className="max-2xl:hidden space-y-[0.417vw] fixed top-1/2 -translate-y-1/2 right-[1.111vw]">
        <Button variant="primary" onClick={handleZoomIn}>
          <div className="2xl:size-[1.111vw] size-4">
            <PlusIcon />
          </div>
        </Button>
        <Button variant="primary" onClick={handleZoomOut}>
          <div className="2xl:size-[1.111vw] size-4">
            <MinusIcon />
          </div>
        </Button>
      </div>
      <FullscreenButton />
    </div>
  );
}

export default SurroundingsPage;
