/* eslint-disable react-hooks/exhaustive-deps */
import { AnimatePresence, motion } from "motion/react";
import { useEffect, useRef, useState } from "react";
import Button from "../ui/Button";
import gsap from "gsap";
import ChevronLeftIcon from "../icons/ChevronLeftIcon";
import ChevronRightIcon from "../icons/ChevronRightIcon";
import Compass from "../Compass";
import { sequenceMasks } from "../../consts/sequenceMasks";
import { isMobile } from "react-device-detect";
import FullscreenButton from "../ui/FullscreenButton";

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

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

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

  // Строгие границы: изображение не должно выходить за пределы контейнера
  // Максимальная позиция: 0 (верхний левый угол контейнера)
  // Минимальная позиция: когда правый/нижний край изображения совпадает с правым/нижним краем контейнера
  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 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 calculateMinZoom = (containerSize: Size, imageSize: Size): number => {
  if (imageSize.width === 0 || imageSize.height === 0) return 0.1;

  // Определяем соотношение сторон контейнера и изображения
  const containerAspectRatio = containerSize.width / containerSize.height;
  const imageAspectRatio = imageSize.width / imageSize.height;

  // Если контейнер шире изображения (горизонтальная ориентация) - заполняем по ширине
  // Если контейнер уже изображения (вертикальная ориентация) - заполняем по высоте
  if (containerAspectRatio > imageAspectRatio) {
    // Горизонтальная ориентация: заполняем по ширине, обрезаем по высоте
    return containerSize.width / imageSize.width;
  } else {
    // Вертикальная ориентация: заполняем по высоте, обрезаем по ширине
    return containerSize.height / imageSize.height;
  }
};

const calculateCenterPosition = (
  containerSize: Size,
  imageSize: Size,
  zoom: number
): Position => {
  const scaledWidth = imageSize.width * zoom;
  const scaledHeight = imageSize.height * zoom;

  return {
    x: (containerSize.width - scaledWidth) / 2,
    y: (containerSize.height - scaledHeight) / 2,
  };
};

const FRAME_COUNT = 4;
const FRAME_STEP = 1;

function MasterplanPage() {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isAnimating, setIsAnimating] = useState(false);
  const [hoveredMaskIndex, setHoveredMaskIndex] = useState<number | null>(null);

  const [imageLoaded, setImageLoaded] = useState(0);
  const [isShowVideo, setIsShowVideo] = useState(true);

  const directionRef = useRef<"next" | "prev">("next");

  function handleImageLoad() {
    setImageLoaded((prev) => prev + 1);
  }

  function handleSwipe(direction: "next" | "prev") {
    if (imageLoaded < FRAME_COUNT) return;
    if (isAnimating) return; // Предотвращаем повторные клики во время анимации

    directionRef.current = direction;
    setIsShowVideo(false);
    setIsAnimating(true);

    // Получаем текущий минимальный зум и размеры контейнера
    const targetZoom = minZoomRef.current;
    const containerSize = containerSizeRef.current;

    // Вычисляем центрированную позицию для минимального зума
    const centerPosition = calculateCenterPosition(
      containerSize,
      originalSize,
      targetZoom
    );
    const constrainedPosition = constrainPosition(
      centerPosition,
      containerSize,
      originalSize,
      targetZoom
    );

    // Проверяем, был ли зум или смещение
    // Сравниваем с небольшой погрешностью для учета ошибок округления
    const zoomThreshold = 0.001;
    const positionThreshold = 1; // пиксели
    const hasZoomChanged = Math.abs(zoom - targetZoom) > zoomThreshold;
    const hasPositionChanged =
      Math.abs(position.x - constrainedPosition.x) > positionThreshold ||
      Math.abs(position.y - constrainedPosition.y) > positionThreshold;

    // Если зума и смещения не было, сразу переключаем кадр
    if (!hasZoomChanged && !hasPositionChanged) return animate(direction);

    // Анимируем зум и позицию до начальных значений
    const zoomTimeline = gsap.timeline({
      onComplete: () => {
        // Сбрасываем флаги после завершения анимации сброса
        // setHasMoved(false);
        // setHasZoomed(false);
        // После завершения анимации зума запускаем смену кадра
        animate(direction);
      },
    });

    // Анимация зума и позиции одновременно
    zoomTimeline.to(
      {
        zoomValue: zoom,
        posX: position.x,
        posY: position.y,
      },
      {
        zoomValue: targetZoom,
        posX: constrainedPosition.x,
        posY: constrainedPosition.y,
        duration: 0.5,
        ease: "power2.inOut",
        onUpdate: function () {
          const currentZoom = this.targets()[0].zoomValue;
          const currentPosX = this.targets()[0].posX;
          const currentPosY = this.targets()[0].posY;

          setZoom(currentZoom);
          setImagePosition({ x: currentPosX, y: currentPosY });
        },
      }
    );
  }

  function animate(direction: "next" | "prev") {
    // isAnimating уже установлен в handleSwipe, не устанавливаем повторно

    const targetIndex =
      currentIndex + (direction === "next" ? FRAME_STEP : -FRAME_STEP);

    gsap.to(
      { value: currentIndex },
      {
        value: targetIndex,
        duration: 0.5,
        ease: "power2.inOut",
        onUpdate: function () {
          const currentValue = Math.round(this.targets()[0].value);

          if (currentValue > FRAME_COUNT - 1 || currentValue < 0) {
            setCurrentIndex(FRAME_COUNT - Math.abs(currentValue));
          } else {
            setCurrentIndex(currentValue);
          }
        },
        onComplete: () => {
          setIsAnimating(false);
          setIsShowVideo(true);
        },
      }
    );
  }

  // const [hoveredFloor, setHoveredFloor] = useState<string | null>(null);
  const [isImageLoaded, setIsImageLoaded] = useState(false);
  // const [isLandscape, setIsLandscape] = useState(true); // Горизонтальная ориентация по умолчанию

  const rootRef = useRef<HTMLDivElement>(null);

  // Image size constants
  const originalSize = { width: 3840, height: 2160 };

  // Zoom and pan state
  const [isDragging, setIsDragging] = useState(false);
  const [startPosition, setStartPosition] = useState<Position>({ x: 0, y: 0 });
  const [position, setImagePosition] = useState<Position>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const containerRef = useRef<SVGSVGElement>(null);
  const previousTouchDistance = useRef<number | null>(null);
  const initialTouchDistance = useRef<number | null>(null);
  const minZoomRef = useRef<number>(0.1); // Минимальный зум будет вычислен динамически
  const maxZoomRef = useRef<number>(1.5); // Максимальный зум будет вычислен динамически
  const maxZoomMultiplier = 1.5; // Коэффициент для максимального зума (50% больше минимального)
  const containerSizeRef = useRef<Size>({ width: 0, height: 0 });
  const lastPanTime = useRef<number>(0);
  const panVelocity = useRef<Position>({ x: 0, y: 0 });

  // Track interaction state to prevent clicks during drag/zoom
  // const [hasMoved, setHasMoved] = useState(false);
  // const [hasZoomed, setHasZoomed] = useState(false);
  const dragStartPosition = useRef<Position>({ x: 0, y: 0 });
  const dragThreshold = 5; // pixels

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

    const containerRect = rootRef.current.getBoundingClientRect();
    const constrainedPosition = constrainPosition(
      newPosition,
      { width: containerRect.width, height: containerRect.height },
      originalSize,
      newZoom
    );

    setImagePosition(constrainedPosition);
  };

  const isAtMaxZoom = zoom >= maxZoomRef.current;
  const isAtMinZoom = zoom <= minZoomRef.current;

  const handleMouseDown = (e: React.MouseEvent<SVGSVGElement>) => {
    if (e.button !== 0) return; // Only handle left mouse button

    setIsDragging(true);
    // setHasMoved(false);
    const { x, y } = getEventPosition(e);
    dragStartPosition.current = { x, y };
    setStartPosition({
      x: x - position.x,
      y: y - position.y,
    });
    lastPanTime.current = Date.now();
    panVelocity.current = { x: 0, y: 0 };
  };

  const handleMouseMove = (e: React.MouseEvent<SVGSVGElement>) => {
    if (!isDragging) return;

    const { x, y } = getEventPosition(e);

    // Check if we've moved beyond the threshold
    const distanceMoved = Math.hypot(
      x - dragStartPosition.current.x,
      y - dragStartPosition.current.y
    );

    if (distanceMoved > dragThreshold) {
      // setHasMoved(true);
    }

    const currentTime = Date.now();
    const deltaTime = currentTime - lastPanTime.current;

    const newPosition = {
      x: x - startPosition.x,
      y: y - startPosition.y,
    };

    // Calculate velocity for momentum
    if (deltaTime > 0) {
      panVelocity.current = {
        x: (newPosition.x - position.x) / deltaTime,
        y: (newPosition.y - position.y) / deltaTime,
      };
    }

    updatePosition(newPosition);
    lastPanTime.current = currentTime;
  };

  const handleMouseUp = () => {
    setIsDragging(false);

    // Apply momentum if velocity is significant
    const velocityThreshold = 0.1;
    if (
      Math.abs(panVelocity.current.x) > velocityThreshold ||
      Math.abs(panVelocity.current.y) > velocityThreshold
    ) {
      const momentum = {
        x: position.x + panVelocity.current.x * 100,
        y: position.y + panVelocity.current.y * 100,
      };

      updatePosition(momentum);
    }

    // Reset interaction state after a short delay
    // setTimeout(() => {
    // setHasMoved(false);
    // setHasZoomed(false);
    // }, 200);
  };

  const handleTouchStart = (e: React.TouchEvent<SVGSVGElement>) => {
    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;
      // setHasZoomed(false);
      return;
    }

    if (e.touches.length === 1) {
      setIsDragging(true);
      // setHasMoved(false);
      const { x, y } = getEventPosition(e);
      dragStartPosition.current = { x, y };
      setStartPosition({
        x: x - position.x,
        y: y - position.y,
      });
      lastPanTime.current = Date.now();
      panVelocity.current = { x: 0, y: 0 };
    }
  };

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

    // setHasMoved(true);
    // setHasZoomed(true);

    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 = distance - previousTouchDistance.current;
      const minDistanceChange = 5; // минимальное изменение в пикселях

      if (Math.abs(distanceChange) >= minDistanceChange) {
        // Zoom based on distance change, not time
        const zoomSensitivity = 0.003; // увеличенная чувствительность зума
        const zoomFactor = 1 + distanceChange * zoomSensitivity;
        const centerX = (touch1.clientX + touch2.clientX) / 2;
        const centerY = (touch1.clientY + touch2.clientY) / 2;

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

        // Prevent zoom if at limits or change is too small
        if (
          Math.abs(newZoom - zoom) > 0.005 &&
          !((zoomFactor > 1 && isAtMaxZoom) || (zoomFactor < 1 && isAtMinZoom))
        ) {
          // setHasZoomed(true);
          // setHasMoved(true);
          setZoom(newZoom);

          const containerRect = rootRef.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);

          // Update previous distance only after successful zoom
          previousTouchDistance.current = distance;
        }
      }
      return;
    }

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

      // Check if we've moved beyond the threshold
      const distanceMoved = Math.hypot(
        x - dragStartPosition.current.x,
        y - dragStartPosition.current.y
      );

      if (distanceMoved > dragThreshold) {
        // setHasMoved(true);
      }

      const currentTime = Date.now();
      const deltaTime = currentTime - lastPanTime.current;

      const newPosition = {
        x: x - startPosition.x,
        y: y - startPosition.y,
      };

      // Calculate velocity for momentum
      if (deltaTime > 0) {
        panVelocity.current = {
          x: (newPosition.x - position.x) / deltaTime,
          y: (newPosition.y - position.y) / deltaTime,
        };
      }

      // Удаляем неиспользуемые переменные и console.log

      updatePosition(newPosition);
      lastPanTime.current = currentTime;
    }
  };

  const handleTouchEnd = () => {
    if (isDragging) {
      // Apply momentum if velocity is significant
      const velocityThreshold = 0.1;
      if (
        Math.abs(panVelocity.current.x) > velocityThreshold ||
        Math.abs(panVelocity.current.y) > velocityThreshold
      ) {
        const momentum = {
          x: position.x + panVelocity.current.x * 100,
          y: position.y + panVelocity.current.y * 100,
        };

        updatePosition(momentum);
      }
    }

    setIsDragging(false);
    previousTouchDistance.current = null;
    initialTouchDistance.current = null;

    // Reset interaction state after a longer delay to prevent accidental clicks after zoom
    // setTimeout(() => {
    // setHasMoved(false);
    // setHasZoomed(false);
    // }, 200);
  };

  const handleWheel = (e: WheelEvent) => {
    e.preventDefault();

    if (!rootRef.current) return;

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

    // Enhanced zoom with better sensitivity
    const zoomSensitivity = 0.001;
    const zoomFactor = 1 - e.deltaY * zoomSensitivity;

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

    // Prevent zoom if at limits or change is too small
    if (
      Math.abs(newZoom - zoom) < 0.005 ||
      (zoomFactor > 1 && isAtMaxZoom) ||
      (zoomFactor < 1 && isAtMinZoom)
    ) {
      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,
    };

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

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

    container.addEventListener("wheel", handleWheel, { passive: false });

    return () => container.removeEventListener("wheel", handleWheel);
  }, [zoom, position]);

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

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

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

      containerSizeRef.current = { width, height };

      // Определяем ориентацию экрана
      // const containerAspectRatio = width / height;
      // const imageAspectRatio = originalSize.width / originalSize.height;
      // const isLandscapeOrientation = containerAspectRatio > imageAspectRatio;
      // setIsLandscape(isLandscapeOrientation);

      // Вычисляем минимальный зум в зависимости от ориентации
      // Для горизонтальной ориентации - по ширине, для вертикальной - по высоте
      const newMinZoom = calculateMinZoom({ width, height }, originalSize);
      minZoomRef.current = newMinZoom;

      // Устанавливаем максимальный зум как минимальный + 50%
      maxZoomRef.current = newMinZoom * maxZoomMultiplier;

      // Инициализируем при первой загрузке или если текущий зум меньше нового минимального
      if (!isImageLoaded || zoom < newMinZoom) {
        const centerPosition = calculateCenterPosition(
          { width, height },
          originalSize,
          newMinZoom // Используем вычисленный минимальный зум
        );
        // Применяем ограничения к центрированной позиции
        const constrainedPosition = constrainPosition(
          centerPosition,
          { width, height },
          originalSize,
          newMinZoom
        );
        setZoom(newMinZoom);
        setImagePosition(constrainedPosition);
        setIsImageLoaded(true);
      } else {
        // При изменении размера окна корректируем позицию, чтобы изображение не выходило за границы
        // Используем функциональное обновление для получения текущей позиции
        setImagePosition((currentPosition) => {
          const constrainedPosition = constrainPosition(
            currentPosition,
            { width, height },
            originalSize,
            zoom
          );
          // Возвращаем ограниченную позицию только если она изменилась
          if (
            constrainedPosition.x !== currentPosition.x ||
            constrainedPosition.y !== currentPosition.y
          ) {
            return constrainedPosition;
          }
          return currentPosition;
        });
      }
    };

    // Initial setup
    setTimeout(updateContainerSize, 0);
    window.addEventListener("resize", updateContainerSize);

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

  useEffect(() => {
    if (!isDragging) return;

    const handleGlobalMouseMove = (e: MouseEvent) => {
      const { x, y } = getEventPosition(e);
      const currentTime = Date.now();
      const deltaTime = currentTime - lastPanTime.current;

      const newPosition = {
        x: x - startPosition.x,
        y: y - startPosition.y,
      };

      // Calculate velocity for momentum
      if (deltaTime > 0)
        panVelocity.current = {
          x: (newPosition.x - position.x) / deltaTime,
          y: (newPosition.y - position.y) / deltaTime,
        };

      updatePosition(newPosition);
      lastPanTime.current = currentTime;
    };

    const handleGlobalMouseUp = () => {
      handleMouseUp();
    };

    document.addEventListener("mousemove", handleGlobalMouseMove);
    document.addEventListener("mouseup", handleGlobalMouseUp);

    return () => {
      document.removeEventListener("mousemove", handleGlobalMouseMove);
      document.removeEventListener("mouseup", handleGlobalMouseUp);
    };
  }, [isMobile, isDragging, startPosition, position]);

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

  return (
    <div className="overflow-hidden relative size-full" ref={rootRef}>
      <div className="size-full relative overflow-hidden">
        <svg
          ref={containerRef}
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 3840 2160"
          className="absolute inset-0 touch-none select-none"
          preserveAspectRatio="xMidYMid slice"
          style={{
            cursor: isDragging ? "grabbing" : "grab",
            ...imageStyle,
          }}
          onMouseDown={handleMouseDown}
          onMouseMove={handleMouseMove}
          onMouseUp={handleMouseUp}
          onTouchStart={handleTouchStart}
          onTouchEnd={handleTouchEnd}
          onTouchCancel={handleTouchEnd}
          onTouchMove={handleTouchMove}
        >
          <defs>
            {hoveredMaskIndex !== null && (
              <mask id="mask-overlay">
                <rect width="3840" height="2160" fill="white" />
                <path
                  d={sequenceMasks[currentIndex][hoveredMaskIndex]}
                  fill="black"
                />
              </mask>
            )}
            {Array.from({ length: FRAME_COUNT }).map((_, index) => (
              <pattern
                {...originalSize}
                key={index}
                id={`bg-image-${index}`}
                patternUnits="userSpaceOnUse"
              >
                <image
                  {...originalSize}
                  href={`/img/masterplan/sequence/${index}.jpg`}
                  onLoad={handleImageLoad}
                />
              </pattern>
            ))}
          </defs>
          {Array.from({ length: FRAME_COUNT }).map((_, index) => (
            <rect
              key={index}
              preserveAspectRatio="none"
              fill={`url(#bg-image-${index})`}
              {...originalSize}
              style={{
                opacity: +(index === currentIndex),
              }}
            />
          ))}
          {/* video in motion.foreignObject */}
          <AnimatePresence>
            {isShowVideo && (
              <motion.image
                key={currentIndex}
                href={`/img/masterplan/sequence/${Math.floor(
                  currentIndex / FRAME_STEP
                )}.jpg`}
                {...originalSize}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                transition={{ duration: 0.2 }}
              />
            )}
          </AnimatePresence>
          <AnimatePresence>
            {hoveredMaskIndex !== null && (
              <motion.rect
                key={hoveredMaskIndex}
                {...originalSize}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                transition={{ duration: 0.5 }}
                fill="#0000004D"
                mask="url(#mask-overlay)"
                pointerEvents="none"
              />
            )}
          </AnimatePresence>

          {sequenceMasks[currentIndex].map((mask, index) => (
            <path
              key={index}
              {...originalSize}
              d={mask}
              className="2xl:stroke-[0.069vw] stroke-white hover:opacity-100 opacity-0 cursor-pointer transition-opacity duration-500 fill-transparent"
              onMouseEnter={() => setHoveredMaskIndex(index)}
              onMouseLeave={() => setHoveredMaskIndex(null)}
            />
          ))}
        </svg>
      </div>

      {imageLoaded === FRAME_COUNT && (
        <>
          <Button
            variant="fab"
            className="absolute top-1/2 -translate-y-1/2 2xl:left-[1.111vw] left-4"
            disabled={isAnimating || !isShowVideo}
            onClick={() => handleSwipe("prev")}
          >
            <span className="2xl:size-[1.111vw] size-4 text-white">
              <ChevronLeftIcon />
            </span>
          </Button>
          <Button
            variant="fab"
            className="absolute top-1/2 -translate-y-1/2 2xl:right-[1.111vw] right-4"
            disabled={isAnimating || !isShowVideo}
            onClick={() => handleSwipe("next")}
          >
            <span className="2xl:size-[1.111vw] size-4 text-white">
              <ChevronRightIcon />
            </span>
          </Button>
          <Compass
            degrees={(currentIndex / FRAME_COUNT) * 360}
            className="absolute 2xl:bottom-[1.111vw] bottom-4 2xl:left-[1.111vw] left-4"
          />
          <FullscreenButton className="absolute 2xl:bottom-[1.111vw] bottom-4 2xl:right-[1.111vw] right-4" />
        </>
      )}
    </div>
  );
}

export default MasterplanPage;
