<script setup>
import { CalendarDate, CalendarDateTime, Time } from "@internationalized/date";
import { useVModel } from "@vueuse/core";
import { format as formatDate, isValid, parse as parseDate } from "date-fns";
import { computed, ref, useTemplateRef } from "vue";
import { cn } from "../../../utils/cn";
import { Popover, PopoverAnchor, PopoverContent } from "../popover";
import DatePickerDatePanel from "./DatePickerDatePanel.vue";
import DatePickerDateTimePanel from "./DatePickerDateTimePanel.vue";
import DatePickerInput from "./DatePickerInput.vue";
import DatePickerMonthPanel from "./DatePickerMonthPanel.vue";
import DatePickerTimeInput from "./DatePickerTimeInput.vue";
import DatePickerYearPanel from "./DatePickerYearPanel.vue";
import PickerShortcuts from "./PickerShortcuts.vue";
import { DATE_PICKER_DEFAULT_FORMATS } from "./types";
defineOptions({ name: "UiDatePicker", inheritAttrs: false });
const props = defineProps({
  modelValue: { type: String, required: false },
  defaultValue: { type: String, required: false },
  type: { type: String, required: false, default: "date" },
  format: { type: String, required: false },
  valueFormat: { type: String, required: false },
  placeholder: { type: String, required: false },
  clearable: { type: Boolean, required: false, default: true },
  disabled: { type: Boolean, required: false },
  readonly: { type: Boolean, required: false },
  disabledDate: { type: Function, required: false },
  timeGranularity: { type: String, required: false, default: "second" },
  hourCycle: { type: Number, required: false, default: 24 },
  class: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
  popoverClass: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
  prefixIcon: { type: String, required: false },
  clearIcon: { type: String, required: false },
  shortcuts: { type: Array, required: false },
  size: { type: String, required: false, default: "md" }
});
const emits = defineEmits(["update:modelValue", "change", "clear", "focus", "blur"]);
const stringValue = useVModel(props, "modelValue", emits, {
  passive: true,
  defaultValue: props.defaultValue
});
const valueFormat = computed(
  () => props.valueFormat ?? DATE_PICKER_DEFAULT_FORMATS[props.type].value
);
const displayFormat = computed(
  () => props.format ?? DATE_PICKER_DEFAULT_FORMATS[props.type].display
);
function pickerValueToNative(value) {
  if (value instanceof Time) {
    const d = new Date(2e3, 0, 1);
    d.setHours(value.hour, value.minute, value.second, 0);
    return d;
  }
  return new Date(
    value.year,
    value.month - 1,
    "day" in value ? value.day : 1,
    "hour" in value ? value.hour : 0,
    "hour" in value ? value.minute : 0,
    "hour" in value ? value.second : 0
  );
}
function nativeToPickerValue(date, type) {
  const y = date.getFullYear();
  const m = date.getMonth() + 1;
  const d = date.getDate();
  const h = date.getHours();
  const mn = date.getMinutes();
  const s = date.getSeconds();
  switch (type) {
    case "year":
      return new CalendarDate(y, 1, 1);
    case "month":
      return new CalendarDate(y, m, 1);
    case "datetime":
      return new CalendarDateTime(y, m, d, h, mn, s);
    case "time":
      return new Time(h, mn, s);
    case "date":
    default:
      return new CalendarDate(y, m, d);
  }
}
const pickerValue = computed(() => {
  const text = stringValue.value;
  if (!text) return void 0;
  const parsed = parseDate(text, valueFormat.value, /* @__PURE__ */ new Date());
  if (!isValid(parsed)) return void 0;
  return nativeToPickerValue(parsed, props.type);
});
function setPickerValue(next) {
  const formatted = next ? formatDate(pickerValueToNative(next), valueFormat.value) : void 0;
  if (formatted === stringValue.value) return;
  stringValue.value = formatted;
  emits("change", formatted);
}
const inputDate = computed(() => {
  const v = pickerValue.value;
  if (!v || v instanceof Time) return void 0;
  return pickerValueToNative(v);
});
const timeValue = computed(() => {
  const v = pickerValue.value;
  return v instanceof Time ? v : void 0;
});
const dateValue = computed(() => {
  const v = pickerValue.value;
  if (!v || v instanceof Time) return void 0;
  return v;
});
const dateTimeValue = computed(() => {
  const v = pickerValue.value;
  if (!v || v instanceof Time || !("hour" in v)) return void 0;
  return v;
});
const open = ref(false);
const anchorEl = useTemplateRef("anchor-el");
function isInsideAnchor(target) {
  if (!(target instanceof Node)) return false;
  return anchorEl.value?.contains(target) ?? false;
}
function onInteractOutside(event) {
  const original = event.detail?.originalEvent;
  const target = original?.target ?? null;
  if (isInsideAnchor(target)) {
    event.preventDefault();
  }
}
function onInputFocus(event) {
  if (!props.disabled && !props.readonly) {
    open.value = true;
  }
  emits("focus", event);
}
function onInputBlur(event) {
  emits("blur", event);
}
function onInputClear() {
  open.value = false;
  emits("clear");
}
function setFromInputDate(d) {
  if (!d) {
    setPickerValue(void 0);
    return;
  }
  setPickerValue(nativeToPickerValue(d, props.type));
}
function setFromInputTime(t) {
  setPickerValue(t);
}
function onPanelUpdate(value, closeAfter) {
  setPickerValue(value);
  if (closeAfter) open.value = false;
}
function applyShortcut(index) {
  const item = props.shortcuts?.[index];
  if (!item) return;
  const raw = typeof item.value === "function" ? item.value() : item.value;
  if (raw === stringValue.value) {
    open.value = false;
    return;
  }
  stringValue.value = raw;
  emits("change", raw);
  open.value = false;
}
</script>

<template>
  <div
    ref="anchor-el"
    data-slot="date-picker"
    :class="cn('w-full', props.class)"
  >
    <DatePickerTimeInput
      v-if="props.type === 'time'"
      v-bind="$attrs"
      :model-value="timeValue"
      :granularity="props.timeGranularity"
      :hour-cycle="props.hourCycle"
      :disabled="props.disabled"
      :readonly="props.readonly"
      :clearable="props.clearable"
      :prefix-icon="props.prefixIcon"
      :clear-icon="props.clearIcon"
      :size="props.size"
      @update:model-value="setFromInputTime"
      @clear="emits('clear')"
    />
    <Popover
      v-else
      v-model:open="open"
    >
      <PopoverAnchor as-child>
        <DatePickerInput
          v-bind="$attrs"
          :model-value="inputDate"
          :format="displayFormat"
          :placeholder="props.placeholder"
          :clearable="props.clearable"
          :disabled="props.disabled"
          :readonly="props.readonly"
          :open="open"
          :prefix-icon="props.prefixIcon"
          :clear-icon="props.clearIcon"
          :size="props.size"
          @update:model-value="setFromInputDate"
          @focus="onInputFocus"
          @blur="onInputBlur"
          @clear="onInputClear"
        />
      </PopoverAnchor>
      <PopoverContent
        align="start"
        :class="cn('w-auto p-0', props.popoverClass)"
        @open-auto-focus="(event) => event.preventDefault()"
        @interact-outside="onInteractOutside"
      >
        <div class="flex flex-col">
          <DatePickerDatePanel
            v-if="props.type === 'date'"
            :model-value="dateValue"
            :is-date-disabled="props.disabledDate"
            @update:model-value="(value) => onPanelUpdate(value, true)"
          />
          <DatePickerMonthPanel
            v-else-if="props.type === 'month'"
            :model-value="dateValue"
            :is-date-disabled="props.disabledDate"
            @update:model-value="(value) => onPanelUpdate(value, true)"
          />
          <DatePickerYearPanel
            v-else-if="props.type === 'year'"
            :model-value="dateValue"
            :is-date-disabled="props.disabledDate"
            @update:model-value="(value) => onPanelUpdate(value, true)"
          />
          <DatePickerDateTimePanel
            v-else-if="props.type === 'datetime'"
            :model-value="dateTimeValue"
            :is-date-disabled="props.disabledDate"
            :granularity="props.timeGranularity"
            :hour-cycle="props.hourCycle"
            @update:model-value="(value) => onPanelUpdate(value, false)"
          />
          <PickerShortcuts
            v-if="props.shortcuts && props.shortcuts.length > 0"
            :items="props.shortcuts"
            @select="applyShortcut"
          />
        </div>
      </PopoverContent>
    </Popover>
  </div>
</template>
