import { getOffset } from './offset.js'

const locks = new Set()

/** Returns the width of the document's scrollbar */
function getScrollbarWidth() {
    const documentWidth = document.documentElement.clientWidth
    return Math.abs(window.innerWidth - documentWidth)
}

/**
 * Used in conjunction with `scrollbarWidth` to set proper body padding in case the user has padding already on the `<body>` element.
 */
function getExistingBodyPadding() {
    const padding = Number(
        getComputedStyle(document.body).paddingRight.replace(/px/, '')
    )

    if (isNaN(padding) || !padding) {
        return 0
    }

    return padding
}

/**
 * Prevents body scrolling. Keeps track of which elements requested a lock so multiple levels of locking are possible
 * without premature unlocking.
 */
export function lockBodyScrolling(lockingEl: HTMLElement) {
    locks.add(lockingEl)

    // When the first lock is created, set the scroll lock size to match the scrollbar's width to prevent content from
    // shifting. We only do this on the first lock because the scrollbar width will measure zero after overflow is hidden.
    if (!document.documentElement.classList.contains('terra-scroll-lock')) {
        /** Scrollbar width + body padding calculation can go away once Safari has scrollbar-gutter support. */
        const scrollbarWidth = getScrollbarWidth() + getExistingBodyPadding() // must be measured before the `terra-scroll-lock` class is applied
        document.documentElement.classList.add('terra-scroll-lock')
        document.documentElement.style.setProperty(
            '--terra-scroll-lock-size',
            `${scrollbarWidth}px`
        )
    }
}

/**
 * Unlocks body scrolling. Scrolling will only be unlocked once all elements that requested a lock call this method.
 */
export function unlockBodyScrolling(lockingEl: HTMLElement) {
    locks.delete(lockingEl)

    if (locks.size === 0) {
        document.documentElement.classList.remove('terra-scroll-lock')
        document.documentElement.style.removeProperty('--terra-scroll-lock-size')
    }
}

/** Scrolls an element into view of its container. If the element is already in view, nothing will happen. */
export function scrollIntoView(
    element: HTMLElement,
    container: HTMLElement,
    direction: 'horizontal' | 'vertical' | 'both' = 'vertical',
    behavior: 'smooth' | 'auto' = 'smooth'
) {
    const offset = getOffset(element, container)
    const offsetTop = offset.top + container.scrollTop
    const offsetLeft = offset.left + container.scrollLeft
    const minX = container.scrollLeft
    const maxX = container.scrollLeft + container.offsetWidth
    const minY = container.scrollTop
    const maxY = container.scrollTop + container.offsetHeight

    if (direction === 'horizontal' || direction === 'both') {
        if (offsetLeft < minX) {
            container.scrollTo({ left: offsetLeft, behavior })
        } else if (offsetLeft + element.clientWidth > maxX) {
            container.scrollTo({
                left: offsetLeft - container.offsetWidth + element.clientWidth,
                behavior,
            })
        }
    }

    if (direction === 'vertical' || direction === 'both') {
        if (offsetTop < minY) {
            container.scrollTo({ top: offsetTop, behavior })
        } else if (offsetTop + element.clientHeight > maxY) {
            container.scrollTo({
                top: offsetTop - container.offsetHeight + element.clientHeight,
                behavior,
            })
        }
    }
}
