<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch, type Slot } from 'vue'
import { useFocusTrap } from '../../composables/useFocusTrap'
import { useHasSlot } from '../../composables/useHasSlots'
import { useModalDragResize } from '../../composables/useModalDragResize'
import { useFdsI18n } from '../../plugin/useFdsI18n'
import FdsButtonIcon from '../Buttons/FdsButtonIcon/FdsButtonIcon.vue'
import FdsIcon from '../FdsIcon/FdsIcon.vue'
import type { FdsModalProps } from './types'

const props = withDefaults(defineProps<FdsModalProps>(), {
  open: false,
  heading: '',
  size: 'md',
  alignTop: false,
  alignLeftFooter: false,
  stickyFooter: false,
  strict: false,
  lockScroll: false,
  movable: false,
  resizable: false,
  info: undefined,
  dataTestid: undefined,
})

const emit = defineEmits<{
  (e: 'close'): void
  (e: 'update:open', value: boolean): void
}>()

const isOpen = ref(props.open)
const previouslyFocusedElement = ref<HTMLElement | null>(null)
const { activate: activateFocusTrap, deactivate: deactivateFocusTrap } = useFocusTrap()

const hasFooterSlot = useHasSlot('modal-footer')
const { t } = useFdsI18n()

const modalInnerRef = ref<HTMLElement | null>(null)

const {
  isCustomPositioned,
  style: dragResizeStyle,
  onDragStart,
  onResizeStart,
  reset: resetDragResize,
  restore: restoreDragResize,
  cleanup: cleanupDragResize,
  shouldBlockBackdropClick,
} = useModalDragResize({
  movable: computed(() => props.movable),
  resizable: computed(() => props.resizable),
  elementRef: modalInnerRef,
  heading: computed(() => props.heading),
})

const modalOuterClasses = computed(() => ['fixed top-0 left-0 w-full h-full overflow-auto z-100'])

const modalInnerClasses = computed(() => {
  const base = [
    'bg-white rounded-lg z-[99999] shadow-lg px-6 pt-6.5',
    props.resizable ? 'overflow-hidden flex flex-col' : 'overflow-auto',
    props.stickyFooter ? 'pb-0' : 'pb-8',
  ]

  if (isCustomPositioned.value) {
    return [...base, 'fixed min-w-[288px] min-h-[200px]']
  }

  return [
    ...base,
    'relative min-w-[288px] w-[calc(100%-5rem)] left-1/2',
    'max-h-[80%] top-1/2 translate-x-[-50%]',
    props.alignTop ? 'top-[10rem]' : 'translate-y-[-50%]',
    {
      'max-w-[480px]': props.size === 'sm',
      'max-w-[588px]': props.size === 'md',
      'max-w-[768px]': props.size === 'lg',
      'max-w-[1200px]': props.size === 'xl',
    },
  ]
})

const headerTitleClasses = computed(() => ['m-0'])

const iconName = computed(() => {
  if (props.info === 'valid') return 'check'
  if (props.info === 'invalid' || props.info === 'warning') return 'alert'
  return null
})

const iconClasses = computed(() => [
  {
    'fill-green-600': props.info === 'valid',
    'fill-red-600': props.info === 'invalid',
    'fill-yellow-600': props.info === 'warning',
  },
])

const closeLabel = computed(() => t('FdsModal.closeModal'))

const footerClasses = computed(() => [
  'mt-6 flex flex-col gap-4 left-0 right-0',
  {
    'sm:flex-row sm:justify-end': !props.alignLeftFooter,
    'sm:flex-row sm:justify-start': props.alignLeftFooter,
    'sticky bg-white bottom-0 shadow-[0px_-8px_16px_0px_rgba(255,255,255,0.59)] z-[2] -mx-6 p-6 mt-0!':
      props.stickyFooter,
  },
])

const setHtmlOverflow = (value: 'auto' | 'hidden') => {
  if (typeof document === 'undefined') return
  const htmlEl = document.documentElement
  if (htmlEl) {
    htmlEl.style.overflow = value
  }
}

const handleBackdropClick = () => {
  if (shouldBlockBackdropClick()) return
  handleClose()
}

const handleClose = () => {
  if (props.strict) {
    return
  }
  isOpen.value = false
  setHtmlOverflow('auto')
  resetDragResize()

  // Restore focus to the element that opened the modal
  nextTick(() => {
    if (previouslyFocusedElement.value) {
      previouslyFocusedElement.value.focus()
      previouslyFocusedElement.value = null
    }
  })

  emit('close')
  emit('update:open', false)
}

const handleKeyDown = (e: KeyboardEvent) => {
  if (isOpen.value && e.code === 'Escape') {
    handleClose()
  }
}

watch(
  () => props.open,
  (newValue) => {
    isOpen.value = newValue
    if (newValue) {
      // Save the currently focused element before opening the modal
      previouslyFocusedElement.value = document.activeElement as HTMLElement
      restoreDragResize()
      if (props.lockScroll) {
        setHtmlOverflow('hidden')
      }
      activateFocusTrap(() => modalInnerRef.value)
    } else {
      setHtmlOverflow('auto')
      resetDragResize()
      deactivateFocusTrap()

      // Restore focus to the element that opened the modal
      nextTick(() => {
        if (previouslyFocusedElement.value) {
          previouslyFocusedElement.value.focus()
          previouslyFocusedElement.value = null
        }
      })
    }
  },
)

onMounted(() => {
  document.addEventListener('keydown', handleKeyDown, true)

  if (props.open) {
    // Save the currently focused element before opening the modal
    previouslyFocusedElement.value = document.activeElement as HTMLElement
    restoreDragResize()

    if (props.lockScroll) {
      setHtmlOverflow('hidden')
    }
    activateFocusTrap(() => modalInnerRef.value)
  }
})

onBeforeUnmount(() => {
  document.removeEventListener('keydown', handleKeyDown, true)
  cleanupDragResize()
  setHtmlOverflow('auto')
  deactivateFocusTrap()
})

const modalAttrs = computed(() => {
  const attrs: Record<string, string> = {}
  if (props.dataTestid) {
    attrs['data-testid'] = props.dataTestid
  }
  return attrs
})

defineSlots<{
  default?: Slot
  'modal-footer'?: Slot
}>()
</script>

<template>
  <Teleport to="body">
    <div v-if="isOpen" @click="handleBackdropClick" :class="modalOuterClasses">
      <!-- Backdrop -->
      <div class="fixed top-0 left-0 w-full h-full bg-black/34 z-99"></div>

      <!-- Modal Inner -->
      <div
        ref="modalInnerRef"
        class="fds-modal-inner"
        :class="[modalInnerClasses, { 'cursor-move select-none': movable }]"
        :style="dragResizeStyle"
        role="alertdialog"
        aria-modal="true"
        v-bind="modalAttrs"
        @click.stop
        @mousedown="onDragStart"
      >
        <!-- Scrollable content wrapper (when resizable, keeps resize handles outside scroll) -->
        <div :class="resizable ? 'overflow-auto flex-1 min-h-0' : ''">
          <div class="flex justify-between mb-4">
            <div class="flex items-center gap-2">
              <FdsIcon v-if="iconName" :name="iconName" :size="24" :class="iconClasses" />
              <h3 tabindex="-1" :class="headerTitleClasses">
                {{ heading }}
              </h3>
            </div>

            <FdsButtonIcon
              v-if="!strict"
              icon="cross"
              :size="28"
              @click="handleClose()"
              v-bind="{ 'aria-label': closeLabel }"
              class="ml-4"
            />
          </div>

          <!-- Content -->
          <div class="mb-0-last-child">
            <slot />
          </div>

          <!-- Footer -->
          <div v-if="hasFooterSlot" :class="footerClasses">
            <slot name="modal-footer" />
          </div>
        </div>

        <!-- Resize handles -->
        <template v-if="resizable">
          <div
            class="absolute top-0 left-3 right-3 h-1.5 cursor-n-resize z-100000"
            @mousedown="onResizeStart($event, 'n')"
          />
          <div
            class="absolute bottom-0 left-3 right-3 h-1.5 cursor-s-resize z-100000"
            @mousedown="onResizeStart($event, 's')"
          />
          <div
            class="absolute left-0 top-3 bottom-3 w-1.5 cursor-w-resize z-100000"
            @mousedown="onResizeStart($event, 'w')"
          />
          <div
            class="absolute right-0 top-3 bottom-3 w-1.5 cursor-e-resize z-100000"
            @mousedown="onResizeStart($event, 'e')"
          />
          <div
            class="absolute top-0 left-0 w-3 h-3 cursor-nw-resize z-100001"
            @mousedown="onResizeStart($event, 'nw')"
          />
          <div
            class="absolute top-0 right-0 w-3 h-3 cursor-ne-resize z-100001"
            @mousedown="onResizeStart($event, 'ne')"
          />
          <div
            class="absolute bottom-0 left-0 w-3 h-3 cursor-sw-resize z-100001"
            @mousedown="onResizeStart($event, 'sw')"
          />
          <div
            class="absolute bottom-0 right-0 w-5 h-5 cursor-se-resize z-100001 flex items-end justify-end p-0.5"
            @mousedown="onResizeStart($event, 'se')"
          >
            <svg class="w-3 h-3 text-gray-500 pointer-events-none" viewBox="0 0 12 12" fill="none">
              <line x1="11" y1="1" x2="1" y2="11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
              <line x1="11" y1="5" x2="5" y2="11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
              <line x1="11" y1="9" x2="9" y2="11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
            </svg>
          </div>
        </template>
      </div>
    </div>
  </Teleport>
</template>
