<template>
  <Teleport to="body">
    <div v-if="isModalOpenRef" class="fixed border-solid border-2 border-gray-500 z-50 flex flex-col backdrop-blur-sm shadow-lg" :style="modalStyle">
      <!-- Header -->
      <div @mousedown="startDrag" class="cursor-move p-2.5 flex justify-between items-center border-solid border-0 border-b border-gray-500 relative">
        <div
          :class="{
            'bg-[url(/assets/ui-texture.png)] bg-no-repeat bg-center bg-cover opacity-90': bgStyle === 'textured',
            'bg-gray': bgStyle !== 'textured'
          }"
          class="rounded-t absolute w-full h-full top-0 left-0"
        />
        <div class="relative z-10">
          <slot name="modalHeader" />
        </div>
        <div class="flex gap-2.5">
          <button v-if="canFullScreen" @click="toggleFullScreen" class="w-5 h-5 m-0 p-0 relative hover:scale-110 transition-transform duration-300 ease-in-out btn-sound">
            <img :alt="isFullScreen ? 'exit full-screen' : 'full-screen'" :src="isFullScreen ? '/assets/icons/modal/minimize.svg' : '/assets/icons/modal/increase-size-option.svg'" class="w-3.5 h-3.5 invert" draggable="false" />
          </button>
          <button v-if="closable" @click="closeModal" class="w-3.5 h-3.5 m-0 p-0 relative hover:rotate-180 transition-transform duration-300 ease-in-out btn-sound">
            <img alt="close" src="/assets/icons/modal/close-button-white.svg" class="w-full h-full btn-sound" draggable="false" />
          </button>
        </div>
      </div>

      <!-- Body -->
      <div class="overflow-hidden grow relative">
        <div
          :class="{
            'bg-[url(/assets/ui-texture.png)] bg-no-repeat bg-center bg-cover opacity-90': bgStyle === 'textured',
            'bg-gray-800': bgStyle === 'dark',
            'bg-gray': bgStyle === 'none'
          }"
          class="rounded-b absolute w-full h-full top-0 left-0"
        />
        <div class="relative z-10 h-full">
          <slot name="modalBody" />
        </div>
        <img v-if="isResizable && !isFullScreen" src="/assets/icons/modal/resize-icon.svg" alt="resize" class="absolute z-10 bottom-0 right-0 w-5 h-5 cursor-nwse-resize" @mousedown="startResize" />
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'

interface ModalProps {
  isModalOpen?: boolean
  closable?: boolean
  isResizable?: boolean
  isFullScreen?: boolean
  canFullScreen?: boolean
  modalPositionX?: number
  modalPositionY?: number
  modalWidth?: number
  modalHeight?: number
  bgStyle?: string
}

interface Position {
  x: number
  y: number
  width: number
  height: number
}

const props = withDefaults(defineProps<ModalProps>(), {
  isModalOpen: false,
  closable: true,
  isResizable: true,
  isFullScreen: false,
  canFullScreen: false,
  modalPositionX: 0,
  modalPositionY: 0,
  modalWidth: 500,
  modalHeight: 280,
  bgStyle: 'textured'
})

const emit = defineEmits<{
  'modal:open': []
  'modal:close': []
}>()

defineExpose({
  open: () => (isModalOpenRef.value = true),
  close: () => (isModalOpenRef.value = false),
  toggle: () => (isModalOpenRef.value = !isModalOpenRef.value),
  setPosition: (a: number, b: number) => {
    x.value = a
    y.value = b
  }
})

const isModalOpenRef = ref(props.isModalOpen)
const width = ref(props.modalWidth)
const height = ref(props.modalHeight)
const x = ref(0)
const y = ref(0)
const isResizing = ref(false)
const isDragging = ref(false)
const isFullScreen = ref(props.isFullScreen)

const minDimensions = {
  width: 200,
  height: 100
}

let dragState = {
  startX: 0,
  startY: 0,
  initialX: 0,
  initialY: 0,
  startWidth: 0,
  startHeight: 0
}

let preFullScreenState: Position = { x: 0, y: 0, width: 0, height: 0 }

const modalStyle = computed(() => ({
  borderRadius: isFullScreen.value ? '0' : '6px',
  top: isFullScreen.value ? '0' : `${y.value}px`,
  left: isFullScreen.value ? '0' : `${x.value}px`,
  width: isFullScreen.value ? '100vw' : `${width.value}px`,
  height: isFullScreen.value ? '100vh' : `${height.value}px`
}))

function startResize(event: MouseEvent) {
  if (isFullScreen.value) return
  isResizing.value = true
  dragState.startWidth = width.value - event.clientX
  dragState.startHeight = height.value - event.clientY
  event.preventDefault()
}

function resizeModal(event: MouseEvent) {
  if (!isResizing.value || isFullScreen.value) return
  width.value = Math.max(dragState.startWidth + event.clientX, minDimensions.width)
  height.value = Math.max(dragState.startHeight + event.clientY, minDimensions.height)
}

function startDrag(event: MouseEvent) {
  if (isFullScreen.value) return
  isDragging.value = true
  dragState = {
    startX: event.clientX,
    startY: event.clientY,
    initialX: x.value,
    initialY: y.value,
    startWidth: width.value,
    startHeight: height.value
  }
  event.preventDefault()
}

function drag(event: MouseEvent) {
  if (!isDragging.value || isFullScreen.value) return
  x.value = dragState.initialX + (event.clientX - dragState.startX)
  y.value = dragState.initialY + (event.clientY - dragState.startY)
}

function closeModal() {
  isModalOpenRef.value = false
  emit('modal:close')
}

function toggleFullScreen() {
  if (isFullScreen.value) {
    Object.assign({ x, y, width, height }, preFullScreenState)
  } else {
    preFullScreenState = { x: x.value, y: y.value, width: width.value, height: height.value }
  }
  isFullScreen.value = !isFullScreen.value
}

function initializePosition() {
  width.value = props.modalWidth
  height.value = props.modalHeight
  x.value = props.modalPositionX || (window.innerWidth - width.value) / 2
  y.value = props.modalPositionY || (window.innerHeight - height.value) / 2
}

// Watchers
watch(
  () => props.isModalOpen,
  (value) => {
    isModalOpenRef.value = value
    if (value) initializePosition()
  }
)

watch(
  () => props.modalWidth,
  (value) => (width.value = value)
)
watch(
  () => props.modalHeight,
  (value) => (height.value = value)
)
watch(
  () => props.modalPositionX,
  (value) => (x.value = value)
)
watch(
  () => props.modalPositionY,
  (value) => (y.value = value)
)

// Lifecycle hooks
onMounted(() => {
  const handlers: Record<string, EventListener[]> = {
    mousemove: [(e: Event) => drag(e as MouseEvent), (e: Event) => resizeModal(e as MouseEvent)],
    mouseup: [
      () => {
        isDragging.value = false
      },
      () => {
        isResizing.value = false
      }
    ]
  }

  Object.entries(handlers).forEach(([event, fns]) => {
    fns.forEach((fn) => window.addEventListener(event, fn))
  })

  initializePosition()
})

onUnmounted(() => {
  const handlers: Record<string, EventListener[]> = {
    mousemove: [(e: Event) => drag(e as MouseEvent), (e: Event) => resizeModal(e as MouseEvent)],
    mouseup: [
      () => {
        isDragging.value = false
      },
      () => {
        isResizing.value = false
      }
    ]
  }

  Object.entries(handlers).forEach(([event, fns]) => {
    fns.forEach((fn) => window.removeEventListener(event, fn))
  })
})
</script>
