<script lang="ts">
    import ZoomControls from "../components/ZoomControls.svelte";

    type ZoomableProps = {
        minZoom?: number;
        maxZoom?: number;
        zoomStep?: number;
        imgUrl: string;
        height?: string | number;
    };

    let {
        minZoom = 0.5,
        maxZoom = 10,
        zoomStep = 0.1,
        imgUrl,
        height,
    }: ZoomableProps = $props();

    let scale = $state(1);

    // DOM refs
    let container = $state<HTMLElement>();
    let image = $state<HTMLElement>();

    // Drag state
    type DragState = {
        startX: number;
        startY: number;
        scrollLeft: number;
        scrollTop: number;
    };

    let dragState: DragState | undefined = $state({
        startX: 0,
        startY: 0,
        scrollLeft: 0,
        scrollTop: 0,
    });

    // Zoom handlers
    function handleZoom(event: WheelEvent) {
        if (!container || !image || !event.ctrlKey) return;

        event.preventDefault();
        const delta = -Math.sign(event.deltaY);
        const newScale = Math.min(
            Math.max(scale + delta * zoomStep, minZoom),
            maxZoom,
        );

        if (newScale !== scale) {
            const rect = image.getBoundingClientRect();
            const x = event.clientX;
            const y = event.clientY;

            const scaleChange = newScale / scale;
            container.scrollLeft = x * scaleChange - x + container.scrollLeft;
            container.scrollTop = y * scaleChange - y + container.scrollTop;

            scale = newScale;
        }
    }

    // Drag handlers
    function handleDragStart(event: MouseEvent) {
        if (!container) return;

        event.preventDefault();
        dragState = {
            startX: event.pageX - container.offsetLeft,
            startY: event.pageY - container.offsetTop,
            scrollLeft: container.scrollLeft,
            scrollTop: container.scrollTop,
        };
    }

    function handleDragMove(event: MouseEvent) {
        if (!container || !dragState) return;

        event.preventDefault();
        const x = event.pageX - container.offsetLeft;
        const y = event.pageY - container.offsetTop;

        container.scrollLeft = dragState.scrollLeft - (x - dragState.startX);
        container.scrollTop = dragState.scrollTop - (y - dragState.startY);
    }

    function handleDragEnd(event: MouseEvent) {
        event.preventDefault();
        dragState = undefined;
    }

    function resetZoom() {
        scale = 1;
        if (container) {
            container.scrollLeft = 0;
            container.scrollTop = 0;
        }
    }

    // Event listener management
    $effect(() => {
        if (container) {
            container.addEventListener("wheel", handleZoom, { passive: false });
            return () => container.removeEventListener("wheel", handleZoom);
        }
    });

</script>

<div class="relative w-full h-full">
    <ZoomControls
        {scale}
        onZoomIn={() => (scale = Math.min(scale + zoomStep, maxZoom))}
        onZoomOut={() => (scale = Math.max(scale - zoomStep, minZoom))}
        {resetZoom}
    />
    <div
        class="container"
        bind:this={container}
        onmousedown={handleDragStart}
        onmousemove={handleDragMove}
        onmouseup={handleDragEnd}
        onmouseleave={handleDragEnd}
        role="presentation"
    >
        <div
            class="image-container"
            bind:this={image}
            style:transform="scale({scale})"
            style:height={height + "px"}
            style:max-height={height + "px"}
        >

            <img
                alt="rendered-page"
                src={imgUrl}
            />
        </div>
    </div>
</div>

<style lang="postcss">
    .container {
        @apply w-full h-full bg-forge-dark;
        overflow: auto;
        cursor: grab;
    }

    .container:active {
        cursor: grabbing;
    }

    .image-container {
        transform-origin: 0 0;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
    }
</style>
