<template>
  <div class="datepicker-field">
    <label v-if="label" :for="inputId" class="datepicker-field__label">{{ label }}</label>

    <div
      :id="inputId"
      ref="referenceEl"
      tabindex="0"
      class="datepicker-field__trigger"
      :class="{
        'datepicker-field__trigger--error': error,
        'datepicker-field__trigger--disabled': disabled,
      }"
      @click="toggle"
    >
      <span class="datepicker-field__value">{{ displayValue }}</span>
      <SvgIcon name="calendar" :size="20" class="datepicker-field__icon" />
    </div>

    <div v-if="error" class="datepicker-field__error" :class="{ 'animate-shake': vibration }">
      {{ error }}
    </div>

    <Teleport to="body">
      <Transition name="fade">
        <div
          v-show="isOpen"
          ref="floatingEl"
          class="date-picker-dropdown datepicker-field__dropdown"
          :style="floatingStyles"
        >
          <DatePicker
            v-model="internalValue"
            locale="fa"
            inline
            editable
            :min="min"
            :max="max"
            :type="type"
            :simple="simple"
            :auto-submit="type === 'date'"
            :format="type === 'datetime' ? 'jYYYY/jMM/jDD HH:mm' : 'jYYYY/jMM/jDD'"
            :display-format="type === 'datetime' ? 'jYYYY/jMM/jDD HH:mm' : 'jYYYY/jMM/jDD'"
            @update:model-value="onSelect"
          />
        </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import DatePicker from 'vue3-persian-datetime-picker'
import { autoUpdate, computePosition, flip, offset, shift, size } from '@floating-ui/dom'

import SvgIcon from '@/components/icons/SvgIcon.vue'
import {
  formatJalaaliDate,
  formatJalaaliDateTime,
  jalaaliStringToIsoDate,
  jalaaliStringToIsoDateTime,
} from '@/utils/date-utils'

let uid = 0
const nextUid = () => `datepicker-field-${++uid}`

const props = defineProps({
  modelValue: { type: String, default: '' },
  name: { type: String, default: '' },
  label: { type: String, default: '' },
  placeholder: { type: String, default: 'یک تاریخ را انتخاب کنید' },
  disabled: { type: Boolean, default: false },
  error: { type: String, default: '' },
  vibration: { type: Boolean, default: false },
  min: { type: String, default: undefined },
  max: { type: String, default: undefined },
  type: { type: String, default: 'date' },
  simple: { type: Boolean, default: false },
})

const emit = defineEmits(['update:modelValue', 'change'])

const inputId = computed(() => props.name || nextUid())
const referenceEl = ref(null)
const floatingEl = ref(null)
const isOpen = ref(false)
const internalValue = ref(null)
const floatingStyles = ref({ position: 'absolute', top: '0px', left: '0px' })
let cleanup = null

const displayValue = computed(() => internalValue.value || props.placeholder)

const syncFromProp = () => {
  const v = props.modelValue
  if (v && typeof v === 'string') {
    internalValue.value =
      props.type === 'datetime' ? formatJalaaliDateTime(v) : formatJalaaliDate(v)
  } else {
    internalValue.value = null
  }
}

watch(() => props.modelValue, syncFromProp, { immediate: true })

const onSelect = (jalaliValue) => {
  if (!jalaliValue) return
  internalValue.value = jalaliValue
  const gregorian =
    props.type === 'datetime'
      ? jalaaliStringToIsoDateTime(jalaliValue)
      : jalaaliStringToIsoDate(jalaliValue)
  emit('update:modelValue', gregorian)
  emit('change', gregorian)
  if (props.type === 'date') close()
}

const mountFloating = () => {
  const reference = referenceEl.value
  const floating = floatingEl.value
  if (!reference || !floating) return
  cleanup = autoUpdate(reference, floating, () => {
    computePosition(reference, floating, {
      placement: 'bottom-start',
      middleware: [
        offset(6),
        flip(),
        shift({ padding: 8 }),
        size({
          apply({ rects }) {
            Object.assign(floating.style, { minWidth: `${rects.reference.width}px` })
          },
        }),
      ],
    }).then(({ x, y }) => {
      floatingStyles.value = { position: 'absolute', left: `${x}px`, top: `${y}px` }
    })
  })
  document.addEventListener('mousedown', handleOutsideClick)
}

const unmountFloating = () => {
  cleanup?.()
  cleanup = null
  document.removeEventListener('mousedown', handleOutsideClick)
}

const handleOutsideClick = (event) => {
  const reference = referenceEl.value
  const floating = floatingEl.value
  if (
    reference &&
    !reference.contains(event.target) &&
    floating &&
    !floating.contains(event.target)
  ) {
    close()
  }
}

const open = () => {
  isOpen.value = true
  setTimeout(mountFloating, 0)
}

const close = () => {
  isOpen.value = false
  unmountFloating()
}

const toggle = () => {
  if (props.disabled) return
  isOpen.value ? close() : open()
}

onBeforeUnmount(() => unmountFloating())
</script>

<style lang="scss" scoped>
.datepicker-field {
  position: relative;
  width: 100%;
  margin-bottom: 0.75rem;

  &__label {
    display: flex;
    margin-bottom: 0.25rem;
    font-family: var(--font-family-fa);
    font-weight: 300;
    line-height: 1.5rem;
    font-size: 0.875rem;
    color: var(--color-prim-gray);
    pointer-events: none;
  }

  &__trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    height: 2.35rem;
    padding: 0 1.25rem;
    border: 1px solid var(--color-thd-gray);
    border-radius: 1.5rem;
    cursor: pointer;
    font-family: var(--font-family-number-fa);
    font-size: 0.9rem;
    transition: all 0.2s;
    outline: none;

    &:focus {
      box-shadow: 0 0 0 1px var(--color-validate);
    }

    &--error {
      border-color: var(--color-error);

      &:focus {
        box-shadow: 0 0 0 1px var(--color-error);
      }
    }

    &--disabled {
      opacity: 0.6;
      cursor: not-allowed;
    }
  }

  &__value {
    flex: 1;
    text-align: start;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &__icon {
    color: var(--color-thd-gray);
    flex-shrink: 0;
  }

  &__error {
    margin-top: 0.25rem;
    font-family: var(--font-family-fa);
    font-size: 0.75rem;
    color: var(--color-error);
  }

  &__dropdown {
    z-index: 50;
    border: 1px solid var(--color-thd-gray);
    border-radius: 0.75rem;
    background: #fff;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 10%), 0 4px 6px -4px rgba(0, 0, 0, 10%);
  }
}
</style>
