<template>
  <component :is="dynamicWrapperComponent">
    <component
      :is="dynamicComponent"
      v-if="dynamicRenderingCondition"
      v-bind="dynamicComponentProps"
      ref="dynamicComponentRef"
      @close="onDynamicComponentClosed"
    >
      <!-- Header slot is only for bottom sheet -->
      <template v-if="title" #header>
        <slot name="title">
          <cp-heading class="cpDynamicDialog__title" heading-level="h3">{{ title }}</cp-heading>
        </slot>
      </template>
      <template v-if="title" #title>
        <slot name="title" />
      </template>
      <template v-if="subtitle" #subtitle>
        <slot name="subtitle" />
      </template>
      <template #default>
        <slot />
      </template>
      <template #footer>
        <slot name="footer" />
      </template>
    </component>
  </component>
</template>

<script setup lang="ts">
import { computed, nextTick, useTemplateRef, watch } from 'vue'

import { useIsBreakpoint } from '@/composables/useIsBreakpoint'

import CpBottomSheet from '@/components/CpBottomSheet.vue'
import CpDialog from '@/components/CpDialog.vue'
import CpTransitionDialog from '@/components/CpTransitionDialog.vue'

import { Breakpoints } from '@/constants/layout'


interface Props {
  animationDuration?: number
  breakpoint?: number
  class?: string
  expandOnContentDrag?: boolean
  halfAndExpand?: boolean
  isClosableOnClickOutside?: boolean
  maxWidth?: number | undefined
  preventClose?: boolean
  snapPoints?: Array<number | `${number}%`>
  subtitle?: string
  swipeCloseThreshold?: string
  title?: string
}

const props = defineProps<Props>()

const emit = defineEmits<{ (e: 'close'): void }>()

const isMobile = useIsBreakpoint(props.breakpoint ?? Breakpoints.MOBILE)

const dynamicComponentRef = useTemplateRef('dynamicComponentRef')

const isVisibleModel = defineModel<boolean>('isVisible', { default: false })

const dynamicWrapperComponent = computed(() => {
  if (isMobile.value) return 'div'
  return CpTransitionDialog
})

const dynamicComponent = computed(() => {
  if (isMobile.value) return CpBottomSheet
  return CpDialog
})

const dynamicRenderingCondition = computed(() => {
  if (isMobile.value) return true
  return isVisibleModel.value
})

const dynamicComponentProps = computed(() => {
  if (isMobile.value) {
    return {
      modelValue: isVisibleModel.value,
      contentClass: props.class,
      canBackdropClose: !props.preventClose,
      canSwipeClose: !props.preventClose,
      expandOnContentDrag: props.expandOnContentDrag,
      preventClose: props.preventClose,
      halfAndExpand: props.halfAndExpand,
      snapPoints: props.snapPoints,
      swipeCloseThreshold: props.swipeCloseThreshold,
      animationDuration: props.animationDuration,
    }
  }

  return {
    maxWidth: props.maxWidth,
    class: props.class,
    preventClose: props.preventClose,
    isClosableOnClickOutside: props.isClosableOnClickOutside,
    ...(props.title && { title: props.title }),
    ...(props.subtitle && { subtitle: props.subtitle }),
  }
})

const onDynamicComponentClosed = () => {
  isVisibleModel.value = false
  emit('close')
}

const openBottomSheet = async () => {
  await nextTick()

  if (!dynamicComponentRef.value) return

  // @ts-expect-error - open does not exist on type {}
  dynamicComponentRef.value?.open()
}

watch(isMobile, () => {
  if (isMobile.value && isVisibleModel.value) {
    openBottomSheet()
  }
})
</script>

<style lang="scss">
.cpDynamicDialog {
  &__title {
    font-size: var(--cp-text-size-2xl);
    font-weight: 700;
    line-height: var(--cp-line-height-2xl);
  }
}
</style>
