import gsap from 'gsap'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { useWindowSize, useEventListener } from '@vueuse/core'

/**
 * Shared global raw mouse state (only set up once)
 */
const rawMouse = {
    x: ref(0),
    y: ref(0),
    initialized: false,
    cleanup: null,
}

const useGlobalMouseListener = () => {
    if (!rawMouse.initialized && !import.meta.env.SSR) {
        rawMouse.initialized = true
        rawMouse.cleanup = useEventListener(window, 'mousemove', (e) => {
            rawMouse.x.value = e.clientX
            rawMouse.y.value = e.clientY
        })
    }
}

/**
 * Composable for smoothed mouse position with customizable smoothing and normalization.
 */
export default (options) => {
    const smoothFactor = options?.smoothFactor ?? 0.1
    const normalize = options?.normalize ?? false
    const callback = options?.onUpdate

    const { width: wWidth, height: wHeight } = useWindowSize()

    const sx = ref(0)
    const sy = ref(0)

    useGlobalMouseListener()

    const getTargetX = () =>
        normalize ? rawMouse.x.value / wWidth.value : rawMouse.x.value
    const getTargetY = () =>
        normalize ? rawMouse.y.value / wHeight.value : rawMouse.y.value

    let tween
    onMounted(() => {
        if (tween) tween.kill

        // Start smoothing tween
        tween = gsap.to(
            { x: sx.value, y: sy.value },
            {
                duration: 1,
                ease: 'linear',
                repeat: -1,
                onUpdate: () => {
                    const newX = gsap.utils.interpolate(
                        sx.value,
                        getTargetX(),
                        smoothFactor,
                    )
                    const newY = gsap.utils.interpolate(
                        sy.value,
                        getTargetY(),
                        smoothFactor,
                    )

                    sx.value = newX
                    sy.value = newY

                    callback?.({ sx: sx.value, sy: sy.value })
                },
            },
        )
    })

    onBeforeUnmount(() => {
        tween?.kill()
        rawMouse.cleanup()
        rawMouse.initialized = false
    })

    return {
        sx,
        sy,
        rawX: rawMouse.x,
        rawY: rawMouse.y,
    }
}
