<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, shallowRef, useTemplateRef, watch } from "vue";
import { cn } from "../../../utils/cn";
import PickerShortcuts from "../date-picker/PickerShortcuts.vue";
import { Popover, PopoverAnchor, PopoverContent } from "../popover";
import DateRangePickerDatePanel from "./DateRangePickerDatePanel.vue";
import DateRangePickerDateTimePanel from "./DateRangePickerDateTimePanel.vue";
import DateRangePickerInput from "./DateRangePickerInput.vue";
import DateRangePickerMonthPanel from "./DateRangePickerMonthPanel.vue";
import DateRangePickerTimeInput from "./DateRangePickerTimeInput.vue";
import DateRangePickerYearPanel from "./DateRangePickerYearPanel.vue";
import { DATE_RANGE_PICKER_DEFAULT_FORMATS } from "./types";
defineOptions({ name: "UiDateRangePicker", inheritAttrs: false });
const props = defineProps({
  modelValue: { type: Array, required: false },
  defaultValue: { type: Array, required: false },
  type: { type: String, required: false, default: "daterange" },
  format: { type: String, required: false },
  valueFormat: { type: String, required: false },
  startPlaceholder: { type: String, required: false },
  endPlaceholder: { type: String, required: false },
  rangeSeparatorIcon: { type: String, required: false },
  prefixIcon: { type: String, required: false },
  clearIcon: { 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 },
  numberOfMonths: { type: Number, required: false, default: 2 },
  timeGranularity: { type: String, required: false, default: "second" },
  hourCycle: { type: Number, required: false, default: 24 },
  shortcuts: { type: Array, required: false },
  size: { type: String, required: false, default: "md" },
  class: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
  popoverClass: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true }
});
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_RANGE_PICKER_DEFAULT_FORMATS[props.type].value
);
const displayFormat = computed(
  () => props.format ?? DATE_RANGE_PICKER_DEFAULT_FORMATS[props.type].display
);
function nativeToDateValue(date, type) {
  const y = date.getFullYear();
  const m = date.getMonth() + 1;
  const d = date.getDate();
  switch (type) {
    case "yearrange":
      return new CalendarDate(y, 1, 1);
    case "monthrange":
      return new CalendarDate(y, m, 1);
    case "datetimerange":
      return new CalendarDateTime(y, m, d, date.getHours(), date.getMinutes(), date.getSeconds());
    case "daterange":
    default:
      return new CalendarDate(y, m, d);
  }
}
function dateValueToNative(value) {
  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 nativeToTime(date) {
  return new Time(date.getHours(), date.getMinutes(), date.getSeconds());
}
function timeToNative(time) {
  const d = new Date(2e3, 0, 1);
  d.setHours(time.hour, time.minute, time.second, 0);
  return d;
}
function parseString(text) {
  if (!text) return void 0;
  const parsed = parseDate(text, valueFormat.value, /* @__PURE__ */ new Date());
  if (!isValid(parsed)) return void 0;
  return parsed;
}
const startNative = computed(() => parseString(stringValue.value?.[0]));
const endNative = computed(() => parseString(stringValue.value?.[1]));
const startTime = computed(
  () => startNative.value ? nativeToTime(startNative.value) : void 0
);
const endTime = computed(
  () => endNative.value ? nativeToTime(endNative.value) : void 0
);
const startDateValue = computed(() => {
  if (!startNative.value) return void 0;
  return nativeToDateValue(startNative.value, props.type);
});
const endDateValue = computed(() => {
  if (!endNative.value) return void 0;
  return nativeToDateValue(endNative.value, props.type);
});
const inProgressRange = shallowRef(void 0);
const rangeValue = computed(() => {
  if (inProgressRange.value) return inProgressRange.value;
  return {
    start: startDateValue.value,
    end: endDateValue.value
  };
});
function emitRange(start, end) {
  if (!start || !end) {
    if (stringValue.value !== void 0) {
      stringValue.value = void 0;
      emits("change", void 0);
    }
    return;
  }
  const startStr = formatDate(start, valueFormat.value);
  const endStr = formatDate(end, valueFormat.value);
  const next = [startStr, endStr];
  const current = stringValue.value;
  if (current && current[0] === next[0] && current[1] === next[1]) return;
  stringValue.value = next;
  emits("change", next);
}
function setStart(date) {
  emitRange(date, endNative.value);
}
function setEnd(date) {
  emitRange(startNative.value, date);
}
function setStartTime(time) {
  emitRange(time ? timeToNative(time) : void 0, endNative.value);
}
function setEndTime(time) {
  emitRange(startNative.value, time ? timeToNative(time) : void 0);
}
function clearAll() {
  if (stringValue.value !== void 0) {
    stringValue.value = void 0;
    emits("change", void 0);
  }
  emits("clear");
}
function applyShortcut(index) {
  const item = props.shortcuts?.[index];
  if (!item) return;
  const raw = typeof item.value === "function" ? item.value() : item.value;
  const next = [raw[0], raw[1]];
  const current = stringValue.value;
  if (current && current[0] === next[0] && current[1] === next[1]) {
    open.value = false;
    return;
  }
  stringValue.value = next;
  emits("change", next);
  open.value = false;
}
function onPanelUpdate(value, closeOnComplete) {
  if (!value || !value.start && !value.end) {
    inProgressRange.value = void 0;
    emitRange(void 0, void 0);
    return;
  }
  if (!value.start || !value.end) {
    inProgressRange.value = value;
    return;
  }
  inProgressRange.value = void 0;
  const startNext = dateValueToNative(value.start);
  const endNext = dateValueToNative(value.end);
  emitRange(startNext, endNext);
  if (closeOnComplete) open.value = false;
}
const open = ref(false);
const anchorEl = useTemplateRef("anchor-el");
watch(open, (isOpen) => {
  if (!isOpen) inProgressRange.value = void 0;
});
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, side) {
  if (!props.disabled && !props.readonly) {
    open.value = true;
  }
  emits("focus", event, side);
}
function onInputBlur(event, side) {
  emits("blur", event, side);
}
</script>

<template>
  <div
    ref="anchor-el"
    data-slot="date-range-picker"
    :class="cn('w-full', props.class)"
  >
    <DateRangePickerTimeInput
      v-if="props.type === 'timerange'"
      v-bind="$attrs"
      :start-value="startTime"
      :end-value="endTime"
      :granularity="props.timeGranularity"
      :hour-cycle="props.hourCycle"
      :range-separator-icon="props.rangeSeparatorIcon"
      :prefix-icon="props.prefixIcon"
      :clear-icon="props.clearIcon"
      :clearable="props.clearable"
      :disabled="props.disabled"
      :readonly="props.readonly"
      :size="props.size"
      @update:start-value="setStartTime"
      @update:end-value="setEndTime"
      @clear="clearAll"
    />
    <Popover
      v-else
      v-model:open="open"
    >
      <PopoverAnchor as-child>
        <DateRangePickerInput
          v-bind="$attrs"
          :start-value="startNative"
          :end-value="endNative"
          :format="displayFormat"
          :start-placeholder="props.startPlaceholder"
          :end-placeholder="props.endPlaceholder"
          :range-separator-icon="props.rangeSeparatorIcon"
          :prefix-icon="props.prefixIcon"
          :clear-icon="props.clearIcon"
          :clearable="props.clearable"
          :disabled="props.disabled"
          :readonly="props.readonly"
          :open="open"
          :size="props.size"
          @update:start-value="setStart"
          @update:end-value="setEnd"
          @focus="onInputFocus"
          @blur="onInputBlur"
          @clear="clearAll"
        />
      </PopoverAnchor>
      <PopoverContent
        align="start"
        :class="cn('w-auto p-0', props.popoverClass)"
        :style="{ minWidth: 'var(--reka-popover-trigger-width)' }"
        @open-auto-focus="(event) => event.preventDefault()"
        @interact-outside="onInteractOutside"
      >
        <div class="flex flex-col">
          <DateRangePickerDatePanel
            v-if="props.type === 'daterange'"
            :model-value="rangeValue"
            :is-date-disabled="props.disabledDate"
            :number-of-months="props.numberOfMonths"
            @update:model-value="(v) => onPanelUpdate(v, true)"
          />
          <DateRangePickerMonthPanel
            v-else-if="props.type === 'monthrange'"
            :model-value="rangeValue"
            :is-date-disabled="props.disabledDate"
            @update:model-value="(v) => onPanelUpdate(v, true)"
          />
          <DateRangePickerYearPanel
            v-else-if="props.type === 'yearrange'"
            :model-value="rangeValue"
            :is-date-disabled="props.disabledDate"
            @update:model-value="(v) => onPanelUpdate(v, true)"
          />
          <DateRangePickerDateTimePanel
            v-else-if="props.type === 'datetimerange'"
            :model-value="rangeValue"
            :is-date-disabled="props.disabledDate"
            :granularity="props.timeGranularity"
            :hour-cycle="props.hourCycle"
            :number-of-months="props.numberOfMonths"
            :range-separator-icon="props.rangeSeparatorIcon"
            @update:model-value="(v) => onPanelUpdate(v, false)"
          />
          <PickerShortcuts
            v-if="props.shortcuts && props.shortcuts.length > 0"
            :items="props.shortcuts"
            @select="applyShortcut"
          />
        </div>
      </PopoverContent>
    </Popover>
  </div>
</template>
