import type { Action } from "svelte/action"

export const spaLink: Action<HTMLAnchorElement> = (node) => {
    const onClick = (event: MouseEvent) => {
        if (node.dataset.spaPrevent && node.dataset.spaPrevent !== "false") {
            return
        }

        const anchor = event.currentTarget as HTMLAnchorElement
        const isSelfTarget = anchor.target === "" || anchor.target === "_self"

        if (isSelfTarget && anchor.href.startsWith(window.location.origin)) {
            event.preventDefault()
            const nextUrl = anchor.pathname + anchor.search + anchor.hash
            const currentUrl = window.location.pathname + window.location.search + window.location.hash

            if (nextUrl === currentUrl) {
                const hashValue = anchor.hash ? anchor.hash.substring(1) : null
                scrollToTarget(hashValue)
                return
            }

            spaNavigate(nextUrl)
        }
    }

    node.addEventListener("click", onClick)

    return {
        destroy() {
            node.removeEventListener("click", onClick)
        },
    }
}

const scrollToTarget = (hash: string | null) => {
    const header = document.getElementById("header-container")
    const headerHeight = header?.getBoundingClientRect().height ?? 0

    if (!hash) {
        window.scrollTo({ top: 0, behavior: "smooth" })
        return
    }

    const decodedHash = decodeURIComponent(hash)

    const tryScroll = (attempt = 0) => {
        const target = document.getElementById(decodedHash)

        if (target) {
            const targetPosition = target.getBoundingClientRect().top + window.scrollY
            const offset = Math.max(targetPosition - headerHeight - 16, 0)
            window.scrollTo({ top: offset, behavior: "smooth" })
            return
        }

        if (attempt < 10) {
            requestAnimationFrame(() => tryScroll(attempt + 1))
        } else {
            window.scrollTo({ top: 0, behavior: "smooth" })
        }
    }

    requestAnimationFrame(() => tryScroll())
}

export const spaNavigate = (to: string, options?: { replace?: boolean }) => {
    const hashIndex = to.indexOf("#")
    const hash = hashIndex >= 0 ? to.slice(hashIndex + 1) : null

    if (options?.replace) {
        window.history.replaceState(null, "", to)
    } else {
        window.history.pushState(null, "", to)
    }

    scrollToTarget(hash)
}

export const spaBack = () => {
    window.history.back()
}
