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

const props = withDefaults(defineProps<FdsDrawerProps>(), {
  open: false,
  heading: '',
  size: 'md',
  strict: false,
  lockScroll: false,
  info: undefined,
  dataTestid: undefined,
})

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

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

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

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

const drawerInnerClasses = computed(() => [
  'bg-white shadow-lg z-[99999] overflow-hidden flex flex-col',
  // Mobile: bottom sheet
  'fixed bottom-0 left-0 right-0 max-h-[90vh] rounded-t-lg',
  // Desktop: right panel
  'md:top-0 md:right-0 md:bottom-auto md:left-auto md:h-full md:max-h-none md:rounded-none md:w-full',
  {
    'md:max-w-[320px]': props.size === 'sm',
    'md:max-w-[400px]': props.size === 'md',
    'md:max-w-[480px]': props.size === 'lg',
    'md:max-w-[588px]': props.size === 'xl',
  },
])

const headerClasses = computed(() => ['shrink-0 flex justify-between bg-white p-6', 'rounded-t-lg md:rounded-none'])

const contentClasses = computed(() => [
  'flex-1 min-h-0 overflow-auto px-6 mb-0-last-child',
  hasFooterSlot.value ? 'pb-4' : 'pb-6',
])

const footerClasses = computed(() => [
  'shrink-0 flex flex-col gap-4 px-6 pb-6 pt-4 bg-white z-[2]',
  'sm:flex-row sm:justify-end',
  'shadow-[0px_-8px_16px_0px_rgba(255,255,255,0.59)]',
])

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('FdsDrawer.closeDrawer'))

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

const handleBackdropClick = () => {
  handleClose()
}

const restoreFocus = () => {
  if (previouslyFocusedElement.value) {
    previouslyFocusedElement.value.focus()
    previouslyFocusedElement.value = null
  }
}

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

  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) {
      previouslyFocusedElement.value = document.activeElement as HTMLElement
      if (props.lockScroll) {
        setHtmlOverflow('hidden')
      }
      activateFocusTrap(() => drawerInnerRef.value)
    } else {
      setHtmlOverflow('auto')
      deactivateFocusTrap()
    }
  },
)

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

  if (props.open) {
    previouslyFocusedElement.value = document.activeElement as HTMLElement

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

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

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

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

<template>
  <Teleport to="body">
    <Transition name="fds-drawer" :duration="{ enter: 280, leave: 180 }" @after-leave="restoreFocus">
      <div v-if="isOpen" @click="handleBackdropClick" :class="drawerOuterClasses">
        <!-- Backdrop -->
        <div class="fds-drawer-backdrop fixed top-0 left-0 w-full h-full bg-black/34 z-99"></div>

        <!-- Drawer Inner -->
        <div
          ref="drawerInnerRef"
          class="fds-drawer-inner"
          :class="drawerInnerClasses"
          role="dialog"
          aria-modal="true"
          v-bind="drawerAttrs"
          @click.stop
        >
          <div :class="headerClasses">
            <div class="flex items-center gap-2 min-w-0">
              <FdsIcon v-if="iconName" :name="iconName" :size="24" :class="iconClasses" />
              <h3 tabindex="-1" :class="headerTitleClasses">
                {{ heading }}
              </h3>
            </div>
            <div>
              <FdsButtonIcon
                v-if="!strict"
                icon="cross"
                :size="28"
                @click="handleClose()"
                v-bind="{ 'aria-label': closeLabel }"
                class="ml-4 shrink-0"
              />
            </div>
          </div>

          <!-- Content -->
          <div :class="contentClasses">
            <slot />
          </div>

          <!-- Footer -->
          <div v-if="hasFooterSlot" :class="footerClasses">
            <slot name="drawer-footer" />
          </div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
/* Slide + fade — mobile: bottom sheet, desktop (md+): right panel */
.fds-drawer-enter-active .fds-drawer-inner,
.fds-drawer-leave-active .fds-drawer-inner {
  transition: transform 280ms cubic-bezier(0.32, 0.72, 0, 1);
}

.fds-drawer-leave-active .fds-drawer-inner {
  transition-duration: 180ms;
}

.fds-drawer-enter-from .fds-drawer-inner,
.fds-drawer-leave-to .fds-drawer-inner {
  transform: translateY(100%);
}

.fds-drawer-enter-active .fds-drawer-backdrop,
.fds-drawer-leave-active .fds-drawer-backdrop {
  transition: opacity 280ms ease;
}

.fds-drawer-leave-active .fds-drawer-backdrop {
  transition-duration: 180ms;
}

.fds-drawer-enter-from .fds-drawer-backdrop,
.fds-drawer-leave-to .fds-drawer-backdrop {
  opacity: 0;
}

@media (min-width: 768px) {
  .fds-drawer-enter-from .fds-drawer-inner,
  .fds-drawer-leave-to .fds-drawer-inner {
    transform: translateX(100%);
  }
}
</style>
