import { useCallback, useEffect, useRef, useState, } from 'react';
import { TRACKPAD_DELTA_Y_THRESHOLD, ZOOM_INTENSITY_MOUSE, ZOOM_INTENSITY_TRACKPAD, ZOOM_MAX, ZOOM_MIN, ZOOM_STEP_IN, ZOOM_STEP_OUT, } from '../../constants';
export const useZoom = ({ scrollContainerRef, enabledZoom, }) => {
    // Текущий масштаб
    const [scale, setScale] = useState(ZOOM_MIN);
    // Флаг для анимации зума
    const [shouldAnimateZoom, setShouldAnimateZoom] = useState(false);
    // Ref для хранения актуального масштаба
    const scaleRef = useRef(scale);
    // Ref для предотвращения одновременных обновлений
    const isUpdatingRef = useRef(false);
    // Для мобильных устройств: начальное расстояние между пальцами и масштаб
    const initialDistanceRef = useRef(null);
    const initialScaleRef = useRef(ZOOM_MIN);
    // Сохраняем позицию верхнего левого угла видимой области для корректного скролла при зуме
    const viewportTopLeftRef = useRef({ x: 0, y: 0 });
    // Синхронизируем ref с состоянием масштаба
    useEffect(() => {
        scaleRef.current = scale;
    }, [scale]);
    /**
     * Сохраняет текущую позицию скролла контейнера
     */
    const saveViewportPosition = useCallback(() => {
        const scrollContainer = scrollContainerRef.current;
        if (!scrollContainer) {
            return;
        }
        viewportTopLeftRef.current = {
            x: scrollContainer.scrollLeft,
            y: scrollContainer.scrollTop,
        };
    }, [scrollContainerRef]);
    /**
     * Масштабирует к заданному значению newScale относительно focusPoint (если задана)
     */
    const zoomTo = useCallback((newScale, focusPoint) => {
        if (!enabledZoom ||
            isUpdatingRef.current ||
            !scrollContainerRef.current) {
            return;
        }
        isUpdatingRef.current = true;
        const scrollContainer = scrollContainerRef.current;
        const prevScale = scaleRef.current;
        // Сохраняем текущую позицию перед изменением масштаба
        saveViewportPosition();
        setScale(newScale);
        scaleRef.current = newScale;
        requestAnimationFrame(() => {
            if (!scrollContainer) {
                isUpdatingRef.current = false;
                return;
            }
            if (focusPoint) {
                // Масштабирование относительно точки курсора/жеста
                const rect = scrollContainer.getBoundingClientRect();
                const mouseX = focusPoint.x - rect.left;
                const mouseY = focusPoint.y - rect.top;
                const contentX = (mouseX + viewportTopLeftRef.current.x) / prevScale;
                const contentY = (mouseY + viewportTopLeftRef.current.y) / prevScale;
                scrollContainer.scrollLeft = contentX * newScale - mouseX;
                scrollContainer.scrollTop = contentY * newScale - mouseY;
            }
            else {
                // Масштабирование относительно левого верхнего угла видимой части
                const contentX = viewportTopLeftRef.current.x / prevScale;
                const contentY = viewportTopLeftRef.current.y / prevScale;
                scrollContainer.scrollLeft = contentX * newScale;
                scrollContainer.scrollTop = contentY * newScale;
            }
            isUpdatingRef.current = false;
        });
    }, [enabledZoom, saveViewportPosition, scrollContainerRef]);
    /**
     * Увеличить масштаб
     */
    const zoomIn = useCallback(() => {
        setShouldAnimateZoom(true);
        zoomTo(Math.min(scaleRef.current * ZOOM_STEP_IN, ZOOM_MAX));
    }, [zoomTo]);
    /**
     * Уменьшить масштаб
     */
    const zoomOut = useCallback(() => {
        setShouldAnimateZoom(true);
        zoomTo(Math.max(scaleRef.current * ZOOM_STEP_OUT, ZOOM_MIN));
    }, [zoomTo]);
    /**
     * Сбросить масштаб к 100%
     */
    const resetZoom = useCallback(() => {
        if (!enabledZoom || !scrollContainerRef.current) {
            return;
        }
        setShouldAnimateZoom(true);
        saveViewportPosition();
        // При сбросе масштаба сохраняем текущую позицию
        const prevScale = scaleRef.current;
        setScale(ZOOM_MIN);
        scaleRef.current = ZOOM_MIN;
        // Возвращаем скролл к сохраненной позиции
        requestAnimationFrame(() => {
            if (scrollContainerRef.current) {
                scrollContainerRef.current.scrollLeft =
                    viewportTopLeftRef.current.x / prevScale;
                scrollContainerRef.current.scrollTop =
                    viewportTopLeftRef.current.y / prevScale;
            }
        });
    }, [enabledZoom, saveViewportPosition, scrollContainerRef]);
    /**
     * Обработчик колеса мыши/тачпада для зума
     */
    const handleWheelCallback = useCallback((event) => {
        if (!event.ctrlKey || !enabledZoom) {
            return;
        }
        event.preventDefault();
        if (isUpdatingRef.current) {
            return;
        }
        setShouldAnimateZoom(false);
        // Определяем, используется ли трекпад (маленькое значение deltaY)
        const isTrackpad = Math.abs(event.deltaY) < TRACKPAD_DELTA_Y_THRESHOLD;
        const zoomIntensity = isTrackpad
            ? ZOOM_INTENSITY_TRACKPAD
            : ZOOM_INTENSITY_MOUSE;
        const delta = event.deltaY > 0 ? 1 / zoomIntensity : zoomIntensity;
        const newScale = Math.max(ZOOM_MIN, Math.min(scaleRef.current * delta, ZOOM_MAX));
        zoomTo(newScale, { x: event.clientX, y: event.clientY });
    }, [enabledZoom, zoomTo]);
    // Навешиваем обработчик колесика мыши
    useEffect(() => {
        const scrollContainer = scrollContainerRef.current;
        if (!scrollContainer) {
            return;
        }
        const handleWheel = (event) => {
            handleWheelCallback(event);
        };
        scrollContainer.addEventListener('wheel', handleWheel, { passive: false });
        return () => {
            scrollContainer.removeEventListener('wheel', handleWheel);
        };
    }, [scrollContainerRef, handleWheelCallback]);
    // Обработчик горячих клавиш для зума
    useEffect(() => {
        const handleKeyDown = (e) => {
            if (!enabledZoom || !e.ctrlKey) {
                return;
            }
            setShouldAnimateZoom(true);
            const keyActions = {
                '0': resetZoom,
                '+': zoomIn,
                '=': zoomIn,
                '-': zoomOut,
            };
            const action = keyActions[e.key];
            if (action) {
                e.preventDefault();
                action();
            }
        };
        window.addEventListener('keydown', handleKeyDown, { passive: false });
        return () => window.removeEventListener('keydown', handleKeyDown);
    }, [zoomIn, zoomOut, resetZoom, enabledZoom]);
    // Обработка pinch-to-zoom для мобильных устройств
    useEffect(() => {
        const scrollContainer = scrollContainerRef.current;
        if (!scrollContainer || !enabledZoom) {
            return;
        }
        // Получить расстояние между двумя пальцами
        const getDistance = (touches) => {
            const dx = touches[0].clientX - touches[1].clientX;
            const dy = touches[0].clientY - touches[1].clientY;
            return Math.sqrt(dx * dx + dy * dy);
        };
        // Получить центр между двумя пальцами
        const getCenter = (touches) => {
            const rect = scrollContainer.getBoundingClientRect();
            return {
                x: (touches[0].clientX + touches[1].clientX) / 2 - rect.left,
                y: (touches[0].clientY + touches[1].clientY) / 2 - rect.top,
            };
        };
        // Начало pinch-жеста
        const onTouchStart = (e) => {
            if (e.touches.length === 2) {
                // Сохраняем начальные значения только при начале жеста
                initialDistanceRef.current = getDistance(e.touches);
                initialScaleRef.current = scaleRef.current;
                setShouldAnimateZoom(false);
            }
        };
        // Движение pinch-жеста
        const onTouchMove = (e) => {
            if (e.touches.length !== 2 || initialDistanceRef.current === null) {
                return;
            }
            e.preventDefault();
            const currentDistance = getDistance(e.touches);
            const center = getCenter(e.touches);
            // Рассчитываем масштаб относительно начального расстояния
            const scaleChange = currentDistance / initialDistanceRef.current;
            const newScale = Math.max(ZOOM_MIN, Math.min(initialScaleRef.current * scaleChange, ZOOM_MAX));
            zoomTo(newScale, center);
        };
        // Завершение pinch-жеста
        const onTouchEnd = () => {
            // Сбрасываем только при окончании жеста
            initialDistanceRef.current = null;
        };
        scrollContainer.addEventListener('touchstart', onTouchStart, {
            passive: false,
        });
        scrollContainer.addEventListener('touchmove', onTouchMove, {
            passive: false,
        });
        scrollContainer.addEventListener('touchend', onTouchEnd);
        scrollContainer.addEventListener('touchcancel', onTouchEnd);
        return () => {
            scrollContainer.removeEventListener('touchstart', onTouchStart);
            scrollContainer.removeEventListener('touchmove', onTouchMove);
            scrollContainer.removeEventListener('touchend', onTouchEnd);
            scrollContainer.removeEventListener('touchcancel', onTouchEnd);
        };
    }, [enabledZoom, scrollContainerRef, zoomTo]);
    return { scale, zoomIn, resetZoom, zoomOut, shouldAnimateZoom };
};
