import { onBeforeUnmount, watch, type WatchSource } from 'vue'

let lockCount = 0
let previousBodyOverflow = ''
let previousBodyOverscrollBehavior = ''
let previousBodyPaddingRight = ''
let previousHtmlOverflow = ''
let previousHtmlOverscrollBehavior = ''

function acquireBodyScrollLock() {
  if (typeof document === 'undefined' || typeof window === 'undefined') return

  if (lockCount === 0) {
    previousBodyOverflow = document.body.style.overflow
    previousBodyOverscrollBehavior = document.body.style.overscrollBehavior
    previousBodyPaddingRight = document.body.style.paddingRight
    previousHtmlOverflow = document.documentElement.style.overflow
    previousHtmlOverscrollBehavior = document.documentElement.style.overscrollBehavior

    const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
    if (scrollbarWidth > 0) {
      document.body.style.paddingRight = `${scrollbarWidth}px`
    }

    document.body.style.overflow = 'hidden'
    document.body.style.overscrollBehavior = 'contain'
    document.documentElement.style.overflow = 'hidden'
    document.documentElement.style.overscrollBehavior = 'contain'
  }

  lockCount += 1
}

function releaseBodyScrollLock() {
  if (typeof document === 'undefined') return
  if (lockCount === 0) return

  lockCount -= 1
  if (lockCount > 0) return

  document.body.style.overflow = previousBodyOverflow
  document.body.style.overscrollBehavior = previousBodyOverscrollBehavior
  document.body.style.paddingRight = previousBodyPaddingRight
  document.documentElement.style.overflow = previousHtmlOverflow
  document.documentElement.style.overscrollBehavior = previousHtmlOverscrollBehavior
}

export function useBodyScrollLock(locked: WatchSource<boolean>) {
  let active = false

  const stop = watch(
    locked,
    (shouldLock) => {
      if (shouldLock && !active) {
        active = true
        acquireBodyScrollLock()
      } else if (!shouldLock && active) {
        active = false
        releaseBodyScrollLock()
      }
    },
    { immediate: true },
  )

  onBeforeUnmount(() => {
    stop()
    if (active) {
      active = false
      releaseBodyScrollLock()
    }
  })
}
