<script lang="ts" setup>
import type { NcButtonProps } from './Button.vue'
import type { NcModalProps } from './Modal.vue'

/**
 * NcModalConfirm component - A customizable modal confirmation dialog.
 *
 * @example
 * ```ts
 * const isOpen = ref(true)
 *
 * const { close } = useDialog(NcModalConfirm, {
 *   'visible': isOpen,
 *   'title': 'Confirm Action',
 *   'content': 'Are you sure you want to proceed?',
 *   'okText': 'Yes',
 *   'cancelText': 'No',
 *   'onCancel': closeDialog,
 *   'onOk': async () => {
 *     closeDialog()
 *     await performAction()
 *   },
 *   'update:visible': closeDialog,
 * })
 *
 * function closeDialog() {
 *   isOpen.value = false
 *   close(1000)
 * }
 * ```
 */

/**
 * Props interface extending NcModalProps with additional customization options.
 */
export interface NcConfirmModalProps extends NcModalProps {
  /** Type of modal (affects icon and styling) */
  type?: 'error' | 'success' | 'warning' | 'info'

  /** Whether to show an icon next to the title */
  showIcon?: boolean

  /** Title of the modal */
  title?: string

  /** Additional class for title styling */
  titleClass?: string

  /** Content of the modal */
  content?: string

  /** Additional class for content styling */
  contentClass?: string

  /** Text for the OK button */
  okText?: string

  /** Additional class for the OK button */
  okClass?: string

  /** Whether to show the OK button */
  showOkBtn?: boolean

  okProps?: Partial<NcButtonProps>

  /** Text for the Cancel button */
  cancelText?: string

  /** Additional class for the Cancel button */
  cancelClass?: string

  /** Whether to show the Cancel button */
  showCancelBtn?: boolean

  cancelProps?: Partial<NcButtonProps>

  /** Extra HTML attributes for the `.nc-modal-confirm` wrapper div */
  wrapperProps?: Record<string, any>

  /** Determines which button gets focus on open */
  focusBtn?: 'ok' | 'cancel' | null
}

const props = withDefaults(defineProps<NcConfirmModalProps>(), {
  maskClosable: false,
  showSeparator: false,
  size: 'xs',
  height: 'auto',

  type: 'warning',
  showIcon: true,

  title: '',
  titleClass: '',

  content: '',
  contentClass: '',

  okText: '',
  okClass: '',
  showOkBtn: true,

  cancelText: '',
  cancelClass: '',
  showCancelBtn: true,

  focusBtn: 'ok',
})

const emits = defineEmits<Emits>()

const { visible: _visible, showOkBtn: _showOkBtn, showCancelBtn: _showCancelBtn, ...restProps } = props

const initialFocus = ref<boolean>(false)

interface Emits {
  (e: 'update:visible', value: boolean): void
  // cancel is generic, on click cancel or close modal using keybord shortcut or overlay click
  (e: 'cancel'): void
  (e: 'clickCancel'): void
  (e: 'ok'): void
}

const vVisible = useVModel(props, 'visible', emits, { defaultValue: false })

const vModel = computed({
  get: () => vVisible.value,
  set: (value: boolean) => {
    vVisible.value = value

    emits('update:visible', value)

    if (!value) {
      emits('cancel')
    }
  },
})

const { type, showCancelBtn, showOkBtn } = toRefs(props)

const iconName = computed<IconMapKey>(() => {
  if (type.value === 'success') {
    return 'circleCheckSolid'
  }

  if (type.value === 'error') {
    return 'ncAlertCircleFilled'
  }

  if (type.value === 'warning') {
    return 'alertTriangleSolid'
  }

  if (type.value === 'info') {
    return 'ncInfoSolid'
  }

  return 'alertTriangleSolid'
})

const cancelBtnRef = ref<HTMLButtonElement>()

const okBtnRef = ref<HTMLButtonElement>()

const onClickCancel = () => {
  vModel.value = false
  emits('clickCancel')
}

/** Watches for cancel button reference and sets focus if applicable */
watch(cancelBtnRef, () => {
  if (!showCancelBtn.value || !cancelBtnRef.value?.$el || props.focusBtn !== 'cancel') return
  ;(cancelBtnRef.value?.$el as HTMLButtonElement)?.focus()
  initialFocus.value = true
})

/** Watches for OK button reference and sets focus if applicable */
watch(okBtnRef, () => {
  if (!showOkBtn.value || !okBtnRef.value?.$el || props.focusBtn !== 'ok') return
  ;(okBtnRef.value?.$el as HTMLButtonElement)?.focus()
  initialFocus.value = true
})

useSelectedCellKeydownListener(
  vModel,
  (e: KeyboardEvent) => {
    switch (e.key) {
      case 'Enter':
        if (
          isActiveInputElementExist() ||
          isActiveButtonOrLinkElementExist() ||
          !document.activeElement?.closest('.nc-modal-confirm-wrapper')
        ) {
          return
        }

        emits('ok')
        break
      case 'Tab':
        if (initialFocus.value) {
          e.preventDefault()

          initialFocus.value = false

          // if focusBtn provided set first focus to the button on first tab
          if (props.focusBtn === 'cancel') {
            cancelBtnRef.value?.$el?.focus()
          } else {
            okBtnRef.value?.$el?.focus()
          }
        }
        break
    }
  },
  {
    immediate: true,
    isGridCell: false,
  },
)
</script>

<template>
  <NcModal v-bind="restProps" v-model:visible="vModel" title="" wrap-class-name="nc-modal-confirm-wrapper">
    <div class="nc-modal-confirm flex flex-col gap-5" :class="[`nc-modal-confirm-type-${type}`]" v-bind="wrapperProps">
      <div class="flex gap-4">
        <div v-if="showIcon" class="nc-modal-confirm-icon-wrapper">
          <slot name="icon">
            <GeneralIcon :icon="iconName" class="nc-confirm-modal-icon" />
          </slot>
        </div>
        <div class="flex-1 flex flex-col gap-2">
          <div class="flex items-start gap-3">
            <div class="nc-modal-confirm-title" :class="titleClass">
              <slot name="title">{{ title }}</slot>
            </div>
            <slot name="headerAction"></slot>
          </div>
          <div v-if="content || $slots.content" class="nc-modal-confirm-content" :class="contentClass">
            <slot name="content">{{ content }}</slot>
          </div>
        </div>
      </div>
      <slot name="extraContent"></slot>

      <div class="flex flex-row w-full justify-end gap-2">
        <NcButton
          v-if="showCancelBtn"
          ref="cancelBtnRef"
          :type="cancelProps?.type ?? 'secondary'"
          size="small"
          class="nc-modal-confirm-cancel-btn"
          :class="cancelClass"
          v-bind="cancelProps"
          :hide-focus="initialFocus"
          @click="onClickCancel"
        >
          {{ cancelText || $t('general.cancel') }}
        </NcButton>
        <NcButton
          v-if="showOkBtn"
          ref="okBtnRef"
          :type="okProps?.type ?? 'primary'"
          size="small"
          class="nc-modal-confirm-ok-btn"
          :class="okClass"
          v-bind="okProps"
          :hide-focus="initialFocus"
          @click="emits('ok')"
        >
          {{ okText || $t('general.ok') }}
        </NcButton>
      </div>
    </div>
  </NcModal>
</template>

<style lang="scss" scoped>
.nc-modal-confirm {
  .nc-modal-confirm-icon-wrapper {
    @apply flex children:flex-none;

    .nc-confirm-modal-icon {
      @apply h-6 w-6;
    }
  }

  .nc-modal-confirm-title {
    @apply text-base text-nc-content-gray font-weight-700 flex-1;
  }

  .nc-modal-confirm-content {
    @apply text-sm text-nc-content-gray-subtle2 font-weight-500 line-clamp-3;
  }

  &.nc-modal-confirm-type-success {
    .nc-modal-confirm-icon-wrapper {
      @apply text-green-700;
    }
  }

  &.nc-modal-confirm-type-error {
    .nc-modal-confirm-icon-wrapper {
      @apply text-red-700;
    }
  }

  &.nc-modal-confirm-type-warning {
    .nc-modal-confirm-icon-wrapper {
      @apply text-orange-500;
    }
  }

  &.nc-modal-confirm-type-info {
    .nc-modal-confirm-icon-wrapper {
      @apply text-nc-content-brand;
    }
  }
}
</style>

<style lang="scss">
.nc-modal-confirm-wrapper {
  @apply z-1050;
}
</style>
