import { FederatedPointerEvent, FederatedWheelEvent } from "pixi.js";
import { ReactNode, useEffect, useState, useRef } from "react";
import { useTransform } from "./transformContext";
import { BACKGROUND_COLOR, SCALE_FACTOR } from "../../assets/Constants";
import { useApplication } from "@pixi/react";
import { useCameraAnimationStore } from "../../stores";
import { observer } from "mobx-react-lite";
import debounce from "lodash/debounce";
import { apiStore } from "../../api/ApiStore/store";

export const InfiniteCanvas = observer(
  ({ children }: Readonly<{ children?: ReactNode }>) => {
    const { route } = apiStore;
    const {
      position,
      setPosition,
      scale,
      setScale,
      setScreenCenter,
      isAutoMode,
      setIsAutoMode,
      userActivityTimestamp,
      updateUserActivity,
      setAutoModeStartTimestamp,
    } = useTransform();
    const [loaded, setLoaded] = useState(false);

    const applicationRef = useApplication();

    const [isDragging, setIsDragging] = useState(false);
    const [startMousePosition, setStartMousePosition] = useState({
      x: 0,
      y: 0,
    });
    const [startPosition, setStartPosition] = useState({ x: 0, y: 0 });

    const activePointers = useRef(new Map<number, { x: number; y: number }>());
    const [isPinching, setIsPinching] = useState(false);
    // Add new useRef for storing initial pinch gesture data
    const pinchStartData = useRef<{
      distance: number;
      midpoint: { x: number; y: number };
      scale: number;
      position: { x: number; y: number };
    } | null>(null);
    // Keep these for backward compatibility, but we'll use pinchStartData for calculations
    const [, setInitialPinchDistance] = useState<number | null>(null);
    const [, setInitialPinchMidpoint] = useState<{
      x: number;
      y: number;
    } | null>(null);

    const [scaleMin, setScaleMin] = useState(0.1); // Default min scale
    const [scaleMax, setScaleMax] = useState(3); // Default max scale
    const cameraAnimationStore = useCameraAnimationStore();

    // Add debounced version of syncState to reduce jittering
    const syncStateDebounced = useRef(
      debounce((pos, zoom) => {
        cameraAnimationStore.syncState(pos, zoom);
      }, 16) // ~60fps
    ).current;

    // Функция для плавного ограничения масштаба
    const getSmoothScale = (targetScale: number, currentScale: number) => {
      if (isAutoMode) return targetScale; // В авто режиме без ограничений

      // Плавное ограничение с затуханием
      const damping = 0.3; // Коэффициент затухания (0-1)

      if (targetScale < scaleMin) {
        // Плавное замедление при приближении к минимальному масштабу
        const distance = scaleMin - targetScale;
        const dampedDistance = distance * damping;
        return Math.max(scaleMin, targetScale + dampedDistance);
      }

      if (targetScale > scaleMax) {
        // Плавное замедление при приближении к максимальному масштабу
        const distance = targetScale - scaleMax;
        const dampedDistance = distance * damping;
        return Math.min(scaleMax, targetScale - dampedDistance);
      }

      // Плавное возвращение к границам, если текущий масштаб за пределами
      if (currentScale < scaleMin) {
        // Плавно возвращаемся к минимальному масштабу
        const distance = scaleMin - currentScale;
        const dampedDistance = distance * damping;
        return Math.min(targetScale, currentScale + dampedDistance);
      }

      if (currentScale > scaleMax) {
        // Плавно возвращаемся к максимальному масштабу
        const distance = currentScale - scaleMax;
        const dampedDistance = distance * damping;
        return Math.max(targetScale, currentScale - dampedDistance);
      }

      return targetScale;
    };

    const handleUserActivity = () => {
      updateUserActivity();
      if (isAutoMode) {
        setIsAutoMode(false);
      }
      // При любом действии пользователя останавливаем анимацию
      cameraAnimationStore.stopAnimation();
    };

    // Автоматический режим - таймер для включения и управление масштабом
    useEffect(() => {
      const interval = setInterval(() => {
        const timeSinceActivity = Date.now() - userActivityTimestamp;
        if (timeSinceActivity >= 5000 && !isAutoMode) {
          // 5 секунд бездействия - включаем авто режим
          if (loaded) {
            setIsAutoMode(true);
            setAutoModeStartTimestamp(Date.now()); // Записываем время включения автопреследования
            // Убираем мгновенную установку масштаба - теперь это делает CameraAnimationStore плавно
          }
        }
      }, 1000); // Проверяем каждую секунду

      return () => clearInterval(interval);
    }, [
      userActivityTimestamp,
      isAutoMode,
      setIsAutoMode,
      setScale,
      setAutoModeStartTimestamp,
    ]);

    useEffect(() => {
      async function fetchRouteData() {
        try {
          const newScaleMin = route?.scale_min! / SCALE_FACTOR;
          const newScaleMax = route?.scale_max! / SCALE_FACTOR;

          setScaleMin(newScaleMin);
          setScaleMax(newScaleMax);
          setLoaded(true);
        } catch (error) {
          console.error(
            "Ошибка загрузки данных маршрута для scaleMin/Max:",
            error
          );
        }
      }
      fetchRouteData();
    }, [route]);

    // Убираем жесткое ограничение масштаба - теперь используется плавное ограничение

    useEffect(() => {
      const canvas = applicationRef?.app.canvas;
      if (!canvas) return;
      const canvasRect = canvas.getBoundingClientRect();
      const canvasLeft = canvasRect.left;
      const canvasTop = canvasRect.top;
      const centerX = window.innerWidth / 2 - canvasLeft;
      const centerY = window.innerHeight / 2 - canvasTop;
      setScreenCenter({ x: centerX, y: centerY });
    }, [applicationRef?.app.canvas, setScreenCenter]);

    const handlePointerDown = (e: FederatedPointerEvent) => {
      handleUserActivity();

      activePointers.current.set(e.pointerId, { x: e.globalX, y: e.globalY });

      if (activePointers.current.size === 1) {
        setIsPinching(false);
        setInitialPinchDistance(null);
        setInitialPinchMidpoint(null);
        pinchStartData.current = null;
        setIsDragging(true);
        setStartPosition({ x: position.x, y: position.y });
        setStartMousePosition({ x: e.globalX, y: e.globalY });
      } else if (activePointers.current.size === 2) {
        setIsDragging(false); // Останавливаем перетаскивание, начинаем пинч
        const pointersArray = Array.from(activePointers.current.values());
        const p1 = pointersArray[0];
        const p2 = pointersArray[1];

        // Calculate initial values
        const initialDistance = Math.hypot(p2.x - p1.x, p2.y - p1.y);
        const initialMidpoint = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };

        // Save initial gesture data in pinchStartData
        pinchStartData.current = {
          distance: initialDistance,
          midpoint: initialMidpoint,
          scale: scale,
          position: { ...position },
        };

        // Keep these for backward compatibility
        setInitialPinchDistance(initialDistance);
        setInitialPinchMidpoint(initialMidpoint);

        setIsPinching(true);
      }
      e.stopPropagation();
    };

    const handlePointerMove = (e: FederatedPointerEvent) => {
      if (!activePointers.current.has(e.pointerId)) return;

      updateUserActivity();
      activePointers.current.set(e.pointerId, { x: e.globalX, y: e.globalY });

      if (
        isPinching &&
        activePointers.current.size === 2 &&
        pinchStartData.current
      ) {
        const pointersArray = Array.from(activePointers.current.values());
        const p1 = pointersArray[0];
        const p2 = pointersArray[1];
        const currentDistance = Math.hypot(p2.x - p1.x, p2.y - p1.y);
        const currentMidpoint = {
          x: (p1.x + p2.x) / 2,
          y: (p1.y + p2.y) / 2,
        };

        // 1. Calculate zoomFactor relative to the INITIAL distance
        const zoomFactor = currentDistance / pinchStartData.current.distance;

        // 2. Calculate new scale relative to the INITIAL scale
        const targetScale = pinchStartData.current.scale * zoomFactor;
        const newScale = getSmoothScale(targetScale, scale);

        // 3. Calculate new position relative to the INITIAL position
        // This is the standard formula for "zoom to point" (in our case, to the midpoint between fingers)
        const newPosition = {
          x:
            pinchStartData.current.midpoint.x +
            (pinchStartData.current.position.x -
              pinchStartData.current.midpoint.x) *
              (newScale / pinchStartData.current.scale),
          y:
            pinchStartData.current.midpoint.y +
            (pinchStartData.current.position.y -
              pinchStartData.current.midpoint.y) *
              (newScale / pinchStartData.current.scale),
        };

        setPosition(newPosition);
        setScale(newScale);
        syncStateDebounced(newPosition, newScale);

        // We do NOT update pinchStartData here - it remains fixed for the entire gesture

        // Update these for backward compatibility, but they're not used for calculations
        setInitialPinchDistance(currentDistance);
        setInitialPinchMidpoint(currentMidpoint);
      } else if (activePointers.current.size === 1) {
        setIsPinching(false);
        setInitialPinchDistance(null);
        setInitialPinchMidpoint(null);
        pinchStartData.current = null;
        
          if (isDragging) {
          const newPosition = {
            x: startPosition.x - startMousePosition.x + e.globalX,
            y: startPosition.y - startMousePosition.y + e.globalY,
          };
          setPosition(newPosition);
          syncStateDebounced(newPosition, scale);
        }
      }
      e.stopPropagation();
    };

    const handlePointerUp = (e: FederatedPointerEvent) => {
      handleUserActivity();

      activePointers.current.delete(e.pointerId);

      if (activePointers.current.size < 2) {
        setIsPinching(false);
        setInitialPinchDistance(null);
        setInitialPinchMidpoint(null);
        pinchStartData.current = null; // Clear pinch gesture data
      }

      if (activePointers.current.size === 0) {
        setIsDragging(false);
      } else if (activePointers.current.size === 1) {
        // If one finger remains after pinch, start dragging from the new position
        const remainingPointer = Array.from(activePointers.current.values())[0];
        setStartPosition({ x: position.x, y: position.y });
        setStartMousePosition({ x: remainingPointer.x, y: remainingPointer.y });
        setIsDragging(true);
      }
      e.stopPropagation();
    };

    const handleWheel = (e: FederatedWheelEvent) => {
      e.stopPropagation();
      handleUserActivity(); // Используем новую функцию

      const mouseX = e.globalX - position.x;
      const mouseY = e.globalY - position.y;
      const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;

      // Используем плавное ограничение масштаба
      const targetScale = scale * zoomFactor;
      const newScale = getSmoothScale(targetScale, scale);
      const actualZoomFactor = newScale / scale;

      if (scale !== newScale && newScale >= scaleMin && newScale <= scaleMax) {
        // Убираем из условия && newScale >= scaleMin && newScale <= scaleMax
        const newPosition = {
          x: position.x + mouseX * (1 - actualZoomFactor),
          y: position.y + mouseY * (1 - actualZoomFactor),
        };
        setPosition(newPosition);
        setScale(newScale);
        // Используем дебаунсированную функцию для синхронизации стора после зума
        syncStateDebounced(newPosition, newScale);
      }
    };

    return (
      <>
        <pixiGraphics
          draw={(g) => {
            const canvas = applicationRef.app.canvas;
            g.clear();
            g.rect(0, 0, canvas?.width ?? 0, canvas?.height ?? 0);
            g.fill(BACKGROUND_COLOR);
          }}
          eventMode="static"
          interactive
          onPointerDown={handlePointerDown}
          onGlobalPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerLeave={handlePointerUp}
          onWheel={handleWheel}
        />
        <pixiContainer position={position} scale={scale}>
          {children}
        </pixiContainer>
      </>
    );
  }
);
