<script lang="tsx">
import { v4 as uuid } from 'uuid'
import {
  Teleport, Transition, computed,
  defineComponent, ref, watch, type PropType,
} from 'vue'
import { useClickOutside } from '../../lib'
import { isLastModalID, modalIds } from '../../utils'
import closeSvg from './assets/icons/close.svg'
import './assets/styles.scss'
import type { ModalWindowSlots, SlotType } from './types'

const transitionParams = {
  appear: true,
  duration: { enter: -50, leave: 0 },
  enterActiveClass: 'active',
  leaveActiveClass: 'leave',
}

/**
 * Компонент модального окна
 */
export default defineComponent({
  inheritAttrs: false,
  props: {
    /**
     * Слоты модального окна { head, body, footer }
     * Тип: string | JSX.Element | Array<JSX.Element> | (() => JSX.Element | Array<JSX.Element>)
     * Не обязательный параметр, можно передавать в функцию open из ref<ModalWindowInterface>
     */
    slots: {
      type: Object as PropType<ModalWindowSlots>,
      default: { head: '', body: '' } as ModalWindowSlots,
    },
    /**
     * Состояние модального окна (открыто/закрыто)
     * Необязательный параметр, дефолтное значение false
     */
    isOpen: {
      type: Boolean,
      default: false,
    },
    /**
     * Изначальное состояние блокировки. Если true, то окно нельзя будет закрыть
     * Необязательный параметр, дефолтное значение false
     */
    isLocked: {
      type: Boolean,
      default: false,
    },
    /**
     * Колбэк-функция отрабатывающая при закрытия модального окна
     */
    onClose: {
      type: Function as PropType<() => void>,
      default: undefined,
    },
  },

  setup(props, { attrs, expose }) {
    const isOpen = ref(props.isOpen)
    const modalWindowSlots = ref<ModalWindowSlots>(props.slots)
    const modalWindowBody = ref<HTMLElement | null>(null)
    const modal = ref<HTMLElement|null>(null)
    const isLocked = ref(true)
    const modalId = uuid()

    if (props.isOpen) modalIds.push(modalId)

    const open = (slots?: ModalWindowSlots) => {
      modalIds.push(modalId)
      isLocked.value = true

      if (slots) modalWindowSlots.value = slots
      isOpen.value = true

      setTimeout(() => { isLocked.value = false }, 100)
    }

    const close = () => {
      if (isLocked.value) return

      isOpen.value = false

      if (props.onClose) props.onClose()
      setTimeout(() => { modalIds.pop() }, 100)
    }

    const closeOnKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        isOpen.value = false
        if (props.onClose) props.onClose()
      }
    }

    const render = (element?: SlotType) => {
      if (!element) return ''

      if (typeof element === 'function') return element()
      return element
    }

    const overflow = computed(() => {
      if (!modalWindowBody.value) return 'visible'
      if ((modalWindowBody.value.offsetHeight / window.innerHeight) * 100 > 70) return 'auto'
      return 'visible'
    })

    if (!props.isLocked) setTimeout(() => { isLocked.value = false }, 100)

    const { class: attrsClass, ...attrsWithoutClass } = attrs

    watch(() => props.isOpen, (value) => (value ? open() : close()))

    watch(isOpen, (value) => {
      if (value) document.addEventListener('keydown', closeOnKey)
      else document.removeEventListener('keydown', closeOnKey)
    })

    useClickOutside(modal, () => {
      if (isLastModalID(modalId) && isOpen.value) {
        close()
      }
    })

    expose({ open, close, isLocked })

    return () => (
      <Teleport to='body'>
        <Transition { ...transitionParams }>
          { (isOpen.value) && (
            <div class={`ark-ui-modal-window ${attrsClass}`} {...attrsWithoutClass}>
              <div ref={modal} class='ark-ui-modal-window-content'>
                <div class='ark-ui-modal-window-header'>
                  { render(modalWindowSlots.value?.head) }
                  <div class='ark-ui-modal-window-close' onClick={close}>
                    { closeSvg }
                  </div>
                </div>
                <div class='ark-ui-modal-window-body' ref={modalWindowBody} style={{ overflowY: overflow.value }}>
                  { render(modalWindowSlots.value?.body) }
                </div>
                { modalWindowSlots.value?.footer ? (
                  <div class='ark-ui-modal-window-footer'>
                    { render(modalWindowSlots.value.footer) }
                  </div>
                ) : '' }
              </div>
            </div>
          ) }
        </Transition>
      </Teleport>
    )
  },
})
</script>
