/* eslint-disable react-hooks/exhaustive-deps */
import clsx from "clsx";
import { useEffect, useRef, useState } from "react";
import {
  surroundingPoints,
  categories,
  CENTER_POINT,
  type ISurroundingPoint,
} from "../../consts/surroundingPoints";
import { usePopupStore } from "../../stores/usePopupStore";
import LocationPopup from "../popups/LocationPopup";
import CategoryFilter from "../ui/CategoryFilter";

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 = 1 }: MapProps) {
  const [originalSize, setOriginalSize] = useState<Size>({
    width: 0,
    height: 0,
  });

  const mapRef = useRef<HTMLImageElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);

  const containerSizeRef = useRef<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 previousTouchDistance = useRef<number | null>(null);
  const initialTouchDistance = useRef<number | null>(null);
  const minZoomRef = useRef<number>(1);
  const markersContainerRef = useRef<HTMLDivElement>(null);
  const [hoveredPoint, setHoveredPoint] = useState<ISurroundingPoint | null>(
    null
  );
  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, setPosition: setPopupPosition, setSide } = usePopupStore();

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

    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(desiredOffsetX, maxOffsetX);
    const boundedOffsetY = Math.min(desiredOffsetY, maxOffsetY);

    setPosition({
      x: -boundedOffsetX,
      y: -boundedOffsetY,
    });
  }, [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 desiredOffsetX = width * 0.35;
      // const desiredOffsetY = height * 0.58;

      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) => {
    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);
  }

  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]);

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

    const containerRect = containerRef.current.getBoundingClientRect();
    const pointX = position.x + point.coordinates.x * zoom;
    const pointY = position.y + point.coordinates.y * zoom;

    const screenX = containerRect.left + pointX;
    const screenY = containerRect.top + pointY;

    setHoveredPoint(point);
    setPopupPosition({ x: screenX, y: screenY });
    setPopup(
      <LocationPopup title={point.title} travelTime={point.travelTime} />
    );

    // Определяем сторону для попапа
    const windowWidth = window.innerWidth;

    if (screenX < windowWidth / 2) {
      setSide("right");
    } else {
      setSide("left");
    }
  };

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

  const handleToggleCategory = (category: string) => {
    const newSelected = new Set(selectedCategories);
    if (newSelected.has(category)) {
      newSelected.delete(category);
    } else {
      newSelected.add(category);
    }
    setSelectedCategories(newSelected);
  };

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

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

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

  return (
    // <div className="relative h-full w-full">
    //   {/* Переключатель категорий */}
    //   <div className="absolute top-4 left-4 z-10 2xl:max-w-[296px] max-w-[296px] bg-[#F7F6F3] 2xl:rounded-2xl rounded-2xl 2xl:p-4 p-4 2xl:shadow-[0px_4px_40px_0px_rgba(15,16,17,0.1),0px_2px_2px_0px_rgba(0,0,0,0.06)] shadow-[0px_4px_40px_0px_rgba(15,16,17,0.1),0px_2px_2px_0px_rgba(0,0,0,0.06)]">
    //     <div className="2xl:space-y-4 space-y-4">
    //       <div className="2xl:space-y-2 space-y-2">
    //         <p className="text-s [font-family:Poppins] font-normal text-[#324D43]">
    //           Baraha Town
    //         </p>
    //         <p className="text-s [font-family:Poppins] font-normal text-[rgba(50,77,67,0.6)]">
    //           Select location on the map
    //         </p>
    //       </div>
    //       <hr className="border-[#E2DCCF] border-[1px]" />
    //       <CategoryFilter
    //         selectedCategories={selectedCategories}
    //         onToggleCategory={handleToggleCategory}
    //       />
    //     </div>
    //   </div>

    <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}
        />
      )}

      {/* SVG для путей из Figma */}
      <svg
        className="absolute pointer-events-none"
        style={imageStyle}
        width={originalSize.width}
        height={originalSize.height}
      >
        {selectedPoint && (
          <>
            {Array.isArray(selectedPoint.path) ? (
              // Если path - массив, отрисовываем два пути
              selectedPoint.path.map((pathData, index) => {
                const color =
                  categories.get(selectedPoint.category) || "#F47F52";
                return (
                  <path
                    key={`route-${selectedPoint.title}-${index}`}
                    d={pathData}
                    fill="none"
                    stroke={index === 0 ? "#FFFFFF" : color}
                    strokeWidth={index === 0 ? 5 : 3}
                    strokeDasharray={index === 0 ? "none" : "6 6"}
                    strokeLinecap="round"
                    className="transition-opacity"
                  />
                );
              })
            ) : (
              // Если path - строка, отрисовываем один путь
              <>
                <path
                  d={selectedPoint.path}
                  fill="none"
                  stroke="#FFFFFF"
                  strokeWidth="5"
                  strokeLinecap="round"
                />
                <path
                  d={selectedPoint.path}
                  fill="none"
                  stroke={categories.get(selectedPoint.category) || "#F47F52"}
                  strokeWidth="3"
                  strokeLinecap="round"
                />
              </>
            )}
          </>
        )}
      </svg>

      {/* Контейнер для точек */}
      <div className="absolute" ref={markersContainerRef} style={imageStyle}>
        <div className="relative h-full">
          {/* Центральная точка (Baraha Town) */}
          <div
            className="absolute transition-all -translate-x-1/2 -translate-y-1/2"
            style={{
              left: CENTER_POINT.x,
              top: CENTER_POINT.y,
              transform: `scale(${maxZoom / zoom})`,
            }}
          >
            <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)] w-max">
              <img
                ref={(el) => {
                  if (!el) return;
                  el.style.minWidth = `${el.naturalWidth}px`;
                }}
                src="/img/surroundings/location.png"
                className="select-none"
                draggable={false}
              />
            </div>
          </div>
          {/* Точки на карте */}
          {filteredPoints.map((point) => {
            const color = categories.get(point.category) || "#F47F52";

            return (
              <div
                key={point.title}
                className="absolute cursor-pointer transition-all"
                style={{
                  left: point.coordinates.x,
                  top: point.coordinates.y,
                  transform: `scale(${maxZoom / zoom})`,
                }}
                onMouseEnter={(e) => handlePointMouseEnter(e, point)}
                onMouseLeave={handlePointMouseLeave}
                onClick={(e) => handlePointClick(e, point)}
              >
                <div
                  className="2xl:size-[1.111] size-4 aspect-square rounded-full 2xl:ring-[0.069vw] ring ring-white transition-all"
                  style={{
                    backgroundColor: color,
                  }}
                />
              </div>
            );
          })}
        </div>
      </div>
    </div>
    // </div>
  );
}

export default SurroundingsPage;
