<script setup lang="ts">

import { twJoin, twMerge } from 'tailwind-merge'
import { onClickOutside, useWindowSize } from '@vueuse/core'
import {disableBodyScroll, enableBodyScroll} from "body-scroll-lock";

const uiConfig = {
  wrapper: 'modal-wrapper relative z-[10000]',
  inner: 'modal-inner fixed inset-0 overflow-y-auto',
  overlay: {
    base: 'fixed inset-0 transition-opacity',
    background: 'bg-deepblue-900/20 dark:bg-gray-800/20 blur-lg',
    transition: {
      'enter-active-class': 'ease-out duration-300',
      'enter-from-class': 'opacity-0',
      'enter-to-class': 'opacity-100',
      'leave-active-class': 'ease-in duration-200',
      'leave-from-class': 'opacity-100',
      'leave-to-class': 'opacity-0'
    }
  },
  base: 'base-modal  fixed  transition-transform max-w-full overflow-auto',
  'base-container': 'text-deepblue-900 dark:text-gray-200 flex flex-col gap-5 bg-white dark:bg-gray-900 h-full w-full',
  before: {
    base: 'absolute cursor-col-resize',
    position: {
      top: 'hidden',
      left: 'top-0 right-0 h-full w-1',
      right: 'top-0 left-0 h-full w-1',
      bottom: 'hidden'
    }
  },
  position: {
    top: 'top-0 left-0 w-full',
    left: 'top-0 left-0 h-full',
    right: 'top-0 right-0 h-full',
    bottom: 'bottom-0 left-0 w-full'
  },
  rounded: {
    top: 'rounded-b-lg',
    bottom: 'rounded-t-lg',
    left: 'rounded-r-lg',
    right: 'rounded-l-lg'
  },
  fullscreen: 'w-screen h-screen rounded-none',
  transition: {
    'enter-active-class': 'transition transform ease-in-out duration-300',
    'leave-active-class': 'transition transform ease-in-out duration-200',
    'enter-from-class': {
      top: 'translate-y-[-100%]',
      left: 'translate-x-[-100%]',
      right: 'translate-x-[100%]',
      bottom: 'translate-y-[100%]'
    },
    'leave-to-class': {
      top: 'translate-y-[-100%]',
      left: 'translate-x-[-100%]',
      right: 'translate-x-[100%]',
      bottom: 'translate-y-[100%]'
    }
  }
}

interface Props {
  title?: string
  transition?: boolean
  overlay?: boolean
  preventClose?: boolean
  fullscreen?: boolean
  name?: string
  size?: string | number
  position?: 'top' | 'left' | 'right' | 'bottom'
  resizable?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  title: '',
  transition: true,
  overlay: true,
  preventClose: false,
  fullscreen: false,
  name: 'slideover',
  size: 424,
  position: 'right',
  resizable: false
})

const slots = useSlots()
const { width } = useWindowSize()
const isMobile = computed(() => width.value < 640)


const modelValue = defineModel<boolean>()

let teleportContainer: HTMLBodyElement | HTMLDivElement | undefined
const slideover = ref<HTMLDivElement | null>(null)

const wrapperClasses = uiConfig.wrapper
const innerClasses = uiConfig.inner
const baseContainerClasses = uiConfig['base-container']
const overlayClasses = computed(() => !props.overlay || twMerge(twJoin(uiConfig.overlay.base, uiConfig.overlay.background)))
const beforeClasses = computed(() => twMerge(twJoin(
  uiConfig.before.base,
  props.resizable ? uiConfig.before.position[props.position] : 'hidden'
)))
const baseClasses = computed(() => twMerge(twJoin(
  uiConfig.base,
  uiConfig.rounded[props.position],
  uiConfig.position[props.position],
  props.fullscreen ? uiConfig.fullscreen : ''
)))
const transitionClasses = computed(() => {
  return {
    overlay: props.transition ? uiConfig.overlay.transition : {},
    base: props.transition ? {
      ...uiConfig.transition,
      'enter-from-class': uiConfig.transition['enter-from-class'][props.position],
      'leave-to-class': uiConfig.transition['leave-to-class'][props.position]
    } : {}
  }
})

const slideoverSize = computed(() => {
  let sideType = 'height'
  if (['left', 'right'].includes(props.position)) {
    sideType = 'width'
  }
  if (props.fullscreen) return
  return ` ${sideType}: ${props.size}${typeof props.size === 'number' ? 'px' : ''}`
})

const onClose = () => {
  modelValue.value = false
}

onClickOutside(slideover, () => props.preventClose || onClose())


type Slot = 'footer' | 'header' | 'content'

const hasSlot = (name: Slot): boolean => {
  return Boolean(slots[name])
}

const hasFooter = computed(() => {
  return hasSlot('footer')
})

const hasHeader = computed(() => {
  return props.title || hasSlot('header')
})

const hasContent = computed(() => {
  return Boolean(slots.default)
})

onBeforeMount(() => {
  const alreadyCreatedTarget = document.getElementById(props.name)
  if (alreadyCreatedTarget) return
  teleportContainer = document.createElement('div')
  teleportContainer.setAttribute('id', props.name)
  document.body.appendChild(teleportContainer)
})


// resize
const BORDER_SIZE = 4
let M_POS: number
const C_POS = ref<number>(-1)

const onResize = (e: MouseEvent) => {
  const panel = slideover.value
  if (!panel) return

  let size: number
  const dx = M_POS - e.x
  M_POS = e.x

  if (props.position === 'left') {
    size = (parseInt(getComputedStyle(panel, '').width) - dx)
  } else if (props.position === 'right') {
    size = (parseInt(getComputedStyle(panel, '').width) + dx)
  }

  if (size >= initialWith.value) {
    C_POS.value = size
    panel.style.width = size + 'px'
  }
}

const onMouseDown = (e: MouseEvent) => {
  if (!process.client && !props.resizable) return

  if (['left', 'right'].includes(props.position) && e.offsetX < BORDER_SIZE) {
    M_POS = e.x
    document.addEventListener('mousemove', onResize, false)
  }
}

const onMouseUp = () => {
  if (!process.client && !props.resizable) return

  document.removeEventListener('mousemove', onResize, false)
}

const initialWith = computed<number>(() => {
  if (typeof props.size === 'number') return props.size
  return parseInt(props.size.replace('px', ''))
})



// Mobile swipe close

const positionStart = ref<number>(0)
const isClose = ref<boolean>(false)
const modalHeight = ref(0)

const touchstart = (event: TouchEvent) => {
  isClose.value = false
  positionStart.value = event.touches[0].clientY
  modalHeight.value = slideover.value?.offsetHeight || 0 // Запоминаем текущую высоту модалки
}

const touchmove = (event: TouchEvent) => {
  if (!isMobile.value) return;
  event.preventDefault();
  const touchPosition = event.touches[0].clientY
  const step = touchPosition - positionStart.value

  if (step < 0) return // предотвратить движение вверх

  const newHeight = modalHeight.value - step
  slideover.value.style.height = `${newHeight}px`
  isClose.value = step > 65
}

const touchend = () => {
  if (isClose.value) {
    onClose()
  } else {
    slideover.value.style.height = `${modalHeight.value}px`
  }
}
const toggleBodyScroll = (lock: boolean) => {
  if (!slideover.value) return
  if (lock) {
    setTimeout(() => {
      disableBodyScroll(slideover.value, {
        reserveScrollBarGap: true,
        allowTouchMove: (el: EventTarget | null): boolean => {
          return el && slideover.value?.contains(el as Node);
        }
      })
    }, 0)

  } else {
    enableBodyScroll(slideover.value)
  }
}

watch(modelValue, async (newValue) => {
  if (newValue) {
    await nextTick()
    toggleBodyScroll(true)
  } else {
    toggleBodyScroll(false)
  }
})
</script>

<template>
  <client-only>
    <teleport :to="`#${name}`">
      <div
        :class="wrapperClasses"
      >
        <Transition
          name="overlay"
          v-bind="transitionClasses.overlay"
        >
          <div
            v-show="modelValue"
            :class="overlayClasses"
          />
        </Transition>
        <Transition
          name="slideover"
          v-bind="transitionClasses.base"
        >
          <div
            v-if="modelValue"
            :class="innerClasses"
          >
            <div
              ref="slideover"
              :class="baseClasses"
              :style="slideoverSize"
              @mousedown="onMouseDown"
              @mouseup="onMouseUp"
            >
              <div :class="beforeClasses" />
              <div :class="baseContainerClasses">
                <div
                  v-if="isMobile && position === 'bottom'"
                  class="w-full flex justify-center items-center mb-2 pt-2 absolute cursor-row-resize "
                  @touchmove="touchmove"
                  @touchstart="touchstart"
                  @touchend="touchend"
                >
                  <div class="h-1 w-10 bg-[#D9D9D9] rounded m-1" />
                </div>
                <div
                  v-if="hasHeader"
                  class="top mt-4"
                  @touchmove="touchmove"
                  @touchstart="touchstart"
                  @touchend="touchend">
                  <h4
                    v-if="title"
                    class="heading-02 text-center"
                  >
                    {{ title }}
                  </h4>
                  <div v-if="!!$slots.header">
                    <slot name="header" />
                  </div>
                </div>
                <div
                  v-if="hasContent"
                  class="grow"
                >
                  <slot />
                </div>
                <div
                  v-if="hasFooter"
                >
                  <slot name="footer" />
                </div>
              </div>
            </div>
          </div>
        </Transition>
      </div>
    </teleport>
  </client-only>
</template>

<style scoped>

</style>
