/* 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 FullscreenButton from "../ui/FullscreenButton";
import { usePopupStore } from "../../stores/usePopupStore";
import { sequencePoints } from "../../consts/blocksPoints";
import { clsx } from "clsx";
import BlockPopup from "../popups/BlockPopup";
import { blocksStats } from "../../consts/blocksStats";
import { isMobile } from "react-device-detect";
import FacilityPopup from "../popups/FacilitiesPopup";
import { facilities } from "../../consts/facilities";
import { facilitiesPoints } from "../../consts/facilitiesPoints";
import { useNavigate } from "react-router";
import LoaderIcon from "../icons/LoaderIcon";

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

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

function 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)),
  };
}

function 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,
  };
}

function 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;
  }
}

function 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 = 360;
// const FRAME_STEP = 90;
const FRAME_COUNT = 120;
const FRAME_STEP = 30;

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");
  const loadedImagesRef = useRef<Set<number>>(new Set());

  function handleImageLoad(index: number) {
    if (loadedImagesRef.current.has(index)) return;
    loadedImagesRef.current.add(index);
    setImageLoaded((prev) => prev + 1);
  }

  // Проверяем загруженность изображений при монтировании компонента
  // Это критично, т.к. изображения в SVG могут не вызывать onLoad при повторном монтировании
  useEffect(() => {
    setImageLoaded(0);
    loadedImagesRef.current.clear();

    // Проверяем каждое изображение на закэшированность
    Array.from({ length: FRAME_COUNT }).forEach((_, index) => {
      const img = new Image();

      img.onload = () => handleImageLoad(index);
      img.onerror = () => handleImageLoad(index); // Даже при ошибке считаем загруженным
      img.src = `/img/masterplan/sequence-120/${index}.jpg`;

      // Если изображение уже закэшировано, img.complete будет true сразу
      if (img.complete) {
        handleImageLoad(index);
      }
    });
  }, []);

  function handleSwipe(direction: "next" | "prev") {
    if (imageLoaded < FRAME_COUNT || 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: () =>
        // После завершения анимации зума запускаем смену кадра
        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") {
    const targetIndex =
      currentIndex + (direction === "next" ? FRAME_STEP : -FRAME_STEP);

    gsap.to(
      { value: currentIndex },
      {
        value: targetIndex,
        duration: 1,
        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 [isImageLoaded, setIsImageLoaded] = useState(false);

  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 dragStartPosition = useRef<Position>({ x: 0, y: 0 });
  const hasImageMoved = useRef<boolean>(false);

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

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

    setImagePosition(constrainedPosition);
  }

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

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

    setIsDragging(true);
    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 };
  }

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

    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;
  }

  function 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);
    }
  }

  function 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;
      hasImageMoved.current = false;
      return;
    }

    if (e.touches.length === 1) {
      setIsDragging(true);
      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 };
      hasImageMoved.current = false;
    }
  }

  function handleTouchMove(e: React.TouchEvent<SVGSVGElement>) {
    if (!rootRef.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 = 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))
        ) {
          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);
          hasImageMoved.current = true;

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

    if (isDragging && e.touches.length === 1) {
      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,
        };

      // Проверяем, было ли перемещение
      const moveThreshold = 5; // порог в пикселях
      const deltaX = Math.abs(newPosition.x - position.x);
      const deltaY = Math.abs(newPosition.y - position.y);
      if (deltaX > moveThreshold || deltaY > moveThreshold) {
        hasImageMoved.current = true;
      }

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

  function 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;
  }

  function handleWheel(e: WheelEvent) {
    e.preventDefault();

    if (!rootRef.current) return;

    setPopup(null);

    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 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
    updateContainerSize();
    addEventListener("resize", updateContainerSize);

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

    addEventListener("mousemove", handleGlobalMouseMove);
    addEventListener("mouseup", handleMouseUp);

    return () => {
      removeEventListener("mousemove", handleGlobalMouseMove);
      removeEventListener("mouseup", handleMouseUp);
    };
  }, [isDragging, startPosition, position]);

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

  useEffect(() => {
    if (!popup) setHoveredMaskIndex(null);
  }, [popup]);

  useEffect(() => setPopup(null), [currentIndex, position]);

  const navigate = useNavigate();

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

  function handleBlockMouseEnter(e: React.MouseEvent, index: number) {
    setHoveredMaskIndex(index);
    setSide("right");
    setAlign("center");
    const rect = e.currentTarget.getBoundingClientRect();
    setParentBoundingClientRect(rect);
    setPopup(
      <BlockPopup
        blockStats={blocksStats[index]}
        number={index + 1}
        onSelect={() => {
          setPopup(null);
          navigate(`/blocks/${index + 1}`);
        }}
      />
    );
  }

  function handleBlockMouseLeave() {
    if (isMobile) return;
    setHoveredMaskIndex(null);
    setPopup(null);
  }

  function handleFacilityLeave() {
    if (isMobile) return;
    setPopup(null);
    setParentBoundingClientRect(null);
  }

  function showFacilityPopup(
    index: number,
    e: React.MouseEvent | React.TouchEvent
  ) {
    setHoveredMaskIndex(null);
    const rect = e.currentTarget.getBoundingClientRect();
    setAlign("start");
    setParentBoundingClientRect(rect);
    setSide("right");
    setPopup(
      <FacilityPopup
        title={facilities[index].title}
        caption={facilities[index].caption}
        description={facilities[index].description}
      />
    );
  }

  return (
    <div className="overflow-hidden relative size-full" ref={rootRef}>
      <AnimatePresence>
        {imageLoaded < FRAME_COUNT && (
          <motion.div
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            className="absolute inset-0 flex flex-col items-center justify-center gap-2 bg-white z-[1]"
          >
            <div className="size-10 text-[#F47F52] animate-spin">
              <LoaderIcon />
            </div>
            <p className="text-[#F47F52] text-m">
              {Math.round((imageLoaded / FRAME_COUNT) * 100)}%
            </p>
          </motion.div>
        )}
      </AnimatePresence>
      <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 overflow-hidden"
          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 && !isAnimating && (
              <mask id="mask-overlay">
                <rect width="3840" height="2160" fill="white" />
                <path
                  d={sequenceMasks[currentIndex / FRAME_STEP][hoveredMaskIndex]}
                  // fill="black"
                />
              </mask>
            )} */}
            {!isAnimating &&
              sequenceMasks[currentIndex / FRAME_STEP].map((mask, index) => (
                <mask id={`mask-${index}`} key={index}>
                  <rect width="3840" height="2160" fill="white" />
                  <path d={mask} 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-120/${index}.jpg`}
                  onLoad={() => handleImageLoad(index)}
                />
              </pattern>
            ))}
          </defs>

          {Array.from({ length: FRAME_COUNT }).map((_, index) => (
            <rect
              key={index}
              preserveAspectRatio="none"
              fill={`url(#bg-image-${index})`}
              {...originalSize}
              style={{
                opacity: +(index === currentIndex),
              }}
            />
          ))}

          {/* {Array.from({ length: FRAME_COUNT }).map((_, index) => (
            <image
              patternUnits="userSpaceOnUse"
              key={index}
              {...originalSize}
              href={`/img/masterplan/sequence-120/${index}.jpg`}
              onLoad={handleImageLoad}
              style={{
                opacity: +(index === currentIndex),
              }}
            />
          ))} */}

          {/* video in motion.foreignObject */}
          <AnimatePresence>
            {isShowVideo && (
              <motion.image
                key={currentIndex}
                href={`/img/masterplan/posters/${Math.floor(
                  currentIndex / FRAME_STEP
                )}.jpg`}
                {...originalSize}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
              />
            )}
          </AnimatePresence>

          <AnimatePresence>
            {hoveredMaskIndex !== null && (
              <motion.rect
                key={hoveredMaskIndex}
                {...originalSize}
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                fill="#0000004D"
                mask={`url(#mask-${hoveredMaskIndex})`}
                pointerEvents="none"
              />
            )}
          </AnimatePresence>

          <AnimatePresence>
            {isShowVideo &&
              hoveredMaskIndex === null &&
              sequencePoints[currentIndex / FRAME_STEP].map(
                ({ x, y }, index) => (
                  <motion.foreignObject
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    exit={{ opacity: 0 }}
                    key={`${currentIndex / FRAME_STEP}-${index}`}
                    className={clsx(
                      "bg-white ring-[#324D4326] 2xl:ring-[0.139vw] ring-2 cursor-pointer 2xl:size-[0.694vw] size-[10px] rounded-full"
                    )}
                    style={{
                      scale: 1 / zoom,
                    }}
                    x={`calc(${x}px - max(0.694vw, 10px) / 2)`}
                    y={`calc(${y}px - max(0.694vw, 10px) / 2)`}
                    width={"max(0.694vw, 10px)"}
                    height={"max(0.694vw, 10px)"}
                  />
                )
              )}
          </AnimatePresence>

          <AnimatePresence>
            {!isAnimating &&
              sequenceMasks[currentIndex / FRAME_STEP].map((mask, index) => (
                <motion.path
                  initial={{ opacity: 0 }}
                  animate={{ opacity: +(hoveredMaskIndex === index) }}
                  exit={{ opacity: 0 }}
                  key={index}
                  d={mask}
                  {...originalSize}
                  className="2xl:stroke-[0.069vw] stroke-[2px] stroke-white cursor-pointer fill-transparent"
                  onMouseEnter={(e) => handleBlockMouseEnter(e, index)}
                  onMouseLeave={handleBlockMouseLeave}
                  onClick={() => {
                    if (isMobile) return;
                    setPopup(null);
                    navigate(`/blocks/${index + 1}`);
                  }}
                />
              ))}
          </AnimatePresence>

          <AnimatePresence>
            {isShowVideo &&
              facilitiesPoints[currentIndex / FRAME_STEP].map(
                ({ x, y, icon }, index) => (
                  <motion.foreignObject
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    exit={{ opacity: 0 }}
                    key={`${currentIndex / FRAME_STEP}-${index}`}
                    className="2xl:p-[0.556vw] p-2 2xl:ring-[0.069vw] ring-1 ring-[#324D4326] backdrop-blur-[8px] 2xl:rounded-[0.556vw] rounded-lg cursor-pointer bg-white"
                    style={{
                      scale: 1 / zoom,
                    }}
                    x={`calc(${x}px - max(2.222vw, 32px) / 2)`}
                    y={`calc(${y}px - max(2.222vw, 32px) / 2)`}
                    width={"max(2.222vw, 32px)"}
                    height={"max(2.222vw, 32px)"}
                    onMouseEnter={(e) => showFacilityPopup(index, e)}
                    onMouseLeave={handleFacilityLeave}
                  >
                    <div className="2xl:size-[1.111vw] size-4">{icon}</div>
                  </motion.foreignObject>
                )
              )}
          </AnimatePresence>
        </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" />
        </>
      )}
      <img
        src="/img/about/baraha_logo.svg"
        className="absolute 2xl:top-[1.111vw] top-4 2xl:right-[1.111vw] right-4 2xl:w-[13.681vw] md:w-[165px] select-none max-md:hidden"
        alt="baraha-logo"
      />
      <img
        src="/img/about/baraha_logo_mini.svg"
        className="absolute size-10 md:hidden select-none top-4 right-4"
        alt="baraha-logo-mobile"
      />
    </div>
  );
}

export default MasterplanPage;
