<template>
  <div
    ref="rootRef"
    class="vm-dt-field"
    :class="{ 'is-disabled': disabled }"
    @click="onRootClick"
  >
    <div class="vm-dt-field__box">
      <span v-if="!displayText" class="vm-dt-field__ph">{{
        placeholder ?? crudLabel('pickDateTime')
      }}</span>
      <span class="vm-dt-field__val">{{ displayText }}</span>
    </div>
    <i class="ri-calendar-line vm-dt-field__icon" aria-hidden="true" />

    <Teleport to="body">
      <!-- FocusScope：暂停宿主 Dialog 焦点陷阱，否则 Teleport 内时分秒 input 无法聚焦手输 -->
      <FocusScope
        v-if="open"
        as-child
        loop
        :trapped="true"
        @unmount-auto-focus="onPanelUnmountAutoFocus"
      >
        <div
          ref="panelWrapRef"
          class="vm-dialog-float"
          :style="panelStyle"
          @pointerdown.stop
          @mousedown.stop
        >
          <VmDateCalendarPanel
            :model-value="modelValue"
            :precision="precision"
            @update:model-value="onPick"
            @clear="onClear"
          />
        </div>
      </FocusScope>
    </Teleport>
  </div>
</template>

<script setup lang="ts">
import { crudLabel } from '../style'
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { FocusScope } from 'reka-ui'
import VmDateCalendarPanel from './vm-date-calendar-panel.vue'
import { POPUP_TRIGGER_GAP } from '../../lib/dialog-float'
import {
  dateTimeDisplayText,
  normalizeDateTimePrecision,
  type DateTimePrecision,
} from '../../../shared/datetime-picker'

defineOptions({ name: 'vm-datetime-field' })

const props = withDefaults(
  defineProps<{
    modelValue?: string | null
    precision?: DateTimePrecision | string
    placeholder?: string
    disabled?: boolean
  }>(),
  {
    modelValue: null,
    precision: 'day',
    placeholder: undefined,
    disabled: false,
  },
)

const emit = defineEmits<{
  'update:modelValue': [v: string | null]
  change: [v: string | null]
}>()

const rootRef = ref<HTMLElement | null>(null)
const panelWrapRef = ref<HTMLElement | null>(null)
const open = ref(false)
const panelStyle = ref<Record<string, string>>({})

const precision = computed(() => normalizeDateTimePrecision(props.precision))
const displayText = computed(() =>
  dateTimeDisplayText(props.modelValue, precision.value),
)

function syncPanelPos() {
  const el = rootRef.value
  if (!el) return
  const r = el.getBoundingClientRect()
  panelStyle.value = {
    position: 'fixed',
    left: `${r.left}px`,
    top: `${r.bottom + POPUP_TRIGGER_GAP}px`,
    zIndex: '10050',
  }
}

function close() {
  open.value = false
}

/** 面板卸载时勿把焦点强行打回触发器（否则会立刻再次 toggle） */
function onPanelUnmountAutoFocus(e: Event) {
  e.preventDefault()
}

function onRootClick() {
  if (props.disabled) return
  if (open.value) {
    close()
    return
  }
  open.value = true
  syncPanelPos()
}

function onPick(v: string) {
  emit('update:modelValue', v)
  emit('change', v)
  if (precision.value !== 'datetime') close()
}

function onClear() {
  emit('update:modelValue', null)
  emit('change', null)
  close()
}

function onDocPointer(e: PointerEvent) {
  if (!open.value) return
  const t = e.target as Node
  if (rootRef.value?.contains(t) || panelWrapRef.value?.contains(t)) return
  close()
}

function onViewportChange() {
  if (open.value) syncPanelPos()
}

onMounted(() => {
  document.addEventListener('pointerdown', onDocPointer, true)
  window.addEventListener('resize', onViewportChange)
  window.addEventListener('scroll', onViewportChange, true)
})

onBeforeUnmount(() => {
  document.removeEventListener('pointerdown', onDocPointer, true)
  window.removeEventListener('resize', onViewportChange)
  window.removeEventListener('scroll', onViewportChange, true)
})
</script>

<style lang="scss" scoped>
.vm-dt-field {
  box-sizing: border-box;
  display: flex;
  width: 100%;
  height: var(--vm-control-height, 38px);
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  border: 1px solid transparent;
  border-radius: 12px;
  background: var(--muted, #f4f6fb);
  cursor: pointer;
  outline: none;

  &.is-disabled {
    opacity: 0.55;
    pointer-events: none;
    cursor: not-allowed;
  }
}

.vm-dt-field__box {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: center;
}

.vm-dt-field__ph {
  position: absolute;
  left: 0;
  right: 0;
  overflow: hidden;
  color: var(--muted-foreground, #94a3b8);
  font-size: inherit;
  text-overflow: ellipsis;
  white-space: nowrap;
  pointer-events: none;
}

.vm-dt-field__val {
  position: relative;
  z-index: 1;
  overflow: hidden;
  color: var(--foreground, #1f2937);
  font-size: inherit;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vm-dt-field__icon {
  flex: none;
  font-size: 16px;
  color: var(--muted-foreground, #94a3b8);
  line-height: 1;
  pointer-events: none;
}
</style>
