const zoomState = new Map();

function getImage(container) {
    return container ? container.querySelector("img.preview-image") : null;
}

function applyTransform(containerId) {
    const state = zoomState.get(containerId);
    if (!state) {
        return;
    }

    const image = getImage(state.container);
    if (!image) {
        return;
    }

    image.style.transform = `translate(${state.posX}px, ${state.posY}px) scale(${state.scale})`;
    image.style.transformOrigin = "center center";
    image.style.cursor = state.scale > 1 ? (state.isDragging ? "grabbing" : "grab") : "default";
}

function clampScale(state) {
    state.scale = Math.min(Math.max(state.scale, state.minScale), state.maxScale);
}

export function setupZoom(containerId) {
    const container = document.getElementById(containerId);
    if (!container) {
        return;
    }

    const existing = zoomState.get(containerId);
    if (existing?.cleanup) {
        existing.cleanup();
    }

    const state = {
        container,
        scale: 1,
        posX: 0,
        posY: 0,
        isDragging: false,
        startX: 0,
        startY: 0,
        minScale: 1,
        maxScale: 8,
        zoomFactor: 1.16,
        cleanup: null
    };

    function handleWheel(e) {
        const image = getImage(container);
        if (!image) {
            return;
        }

        e.preventDefault();

        const rect = container.getBoundingClientRect();
        const mouseX = e.clientX - rect.left - rect.width / 2;
        const mouseY = e.clientY - rect.top - rect.height / 2;
        const nextScale = e.deltaY < 0 ? state.scale * state.zoomFactor : state.scale / state.zoomFactor;
        const clampedScale = Math.min(Math.max(nextScale, state.minScale), state.maxScale);
        const zoomRatio = clampedScale / state.scale;

        state.posX = mouseX - (mouseX - state.posX) * zoomRatio;
        state.posY = mouseY - (mouseY - state.posY) * zoomRatio;
        state.scale = clampedScale;

        if (state.scale <= 1.001) {
            state.posX = 0;
            state.posY = 0;
        }

        applyTransform(containerId);
    }

    function handleMouseDown(e) {
        if (state.scale <= 1) {
            return;
        }

        state.isDragging = true;
        state.startX = e.clientX - state.posX;
        state.startY = e.clientY - state.posY;
        applyTransform(containerId);
    }

    function handleMouseMove(e) {
        if (!state.isDragging) {
            return;
        }

        state.posX = e.clientX - state.startX;
        state.posY = e.clientY - state.startY;
        applyTransform(containerId);
    }

    function handleMouseUp() {
        if (!state.isDragging) {
            return;
        }

        state.isDragging = false;
        applyTransform(containerId);
    }

    function handleTouchStart(e) {
        if (e.touches.length !== 1 || state.scale <= 1) {
            return;
        }

        state.isDragging = true;
        state.startX = e.touches[0].clientX - state.posX;
        state.startY = e.touches[0].clientY - state.posY;
    }

    function handleTouchMove(e) {
        if (!state.isDragging || e.touches.length !== 1) {
            return;
        }

        e.preventDefault();
        state.posX = e.touches[0].clientX - state.startX;
        state.posY = e.touches[0].clientY - state.startY;
        applyTransform(containerId);
    }

    function handleTouchEnd() {
        state.isDragging = false;
        applyTransform(containerId);
    }

    const options = { passive: false };
    container.addEventListener("wheel", handleWheel, options);
    container.addEventListener("mousedown", handleMouseDown, options);
    container.addEventListener("mousemove", handleMouseMove, options);
    container.addEventListener("mouseup", handleMouseUp, options);
    container.addEventListener("mouseleave", handleMouseUp, options);
    container.addEventListener("touchstart", handleTouchStart, options);
    container.addEventListener("touchmove", handleTouchMove, options);
    container.addEventListener("touchend", handleTouchEnd, options);
    container.addEventListener("touchcancel", handleTouchEnd, options);

    state.cleanup = () => {
        container.removeEventListener("wheel", handleWheel, options);
        container.removeEventListener("mousedown", handleMouseDown, options);
        container.removeEventListener("mousemove", handleMouseMove, options);
        container.removeEventListener("mouseup", handleMouseUp, options);
        container.removeEventListener("mouseleave", handleMouseUp, options);
        container.removeEventListener("touchstart", handleTouchStart, options);
        container.removeEventListener("touchmove", handleTouchMove, options);
        container.removeEventListener("touchend", handleTouchEnd, options);
        container.removeEventListener("touchcancel", handleTouchEnd, options);
        zoomState.delete(containerId);
    };

    zoomState.set(containerId, state);
    applyTransform(containerId);
}

export function zoomIn(containerId) {
    const state = zoomState.get(containerId);
    if (!state) {
        return;
    }

    state.scale *= state.zoomFactor;
    clampScale(state);
    applyTransform(containerId);
}

export function zoomOut(containerId) {
    const state = zoomState.get(containerId);
    if (!state) {
        return;
    }

    state.scale /= state.zoomFactor;
    clampScale(state);

    if (state.scale <= 1.001) {
        state.scale = 1;
        state.posX = 0;
        state.posY = 0;
    }

    applyTransform(containerId);
}

export function resetZoom(containerId) {
    const state = zoomState.get(containerId);
    if (!state) {
        return;
    }

    state.scale = 1;
    state.posX = 0;
    state.posY = 0;
    state.isDragging = false;
    applyTransform(containerId);
}
