<script setup>
import { ref, computed, watch, watchEffect, nextTick, useAttrs } from "vue";
import { union, isBoolean, isArray, isString, omit } from "lodash-es";
import { VTextField } from "vuetify/components/VTextField";
import { Datetime } from "../../utils/datetime";
import { useRules } from "../../composables/utils/validation";
const props = defineProps({
  modelValue: { type: [String, null], required: false },
  format: { type: String, required: false, default: "shortDate" },
  label: { type: String, required: false },
  enableSeconds: { type: Boolean, required: false, default: false },
  rules: { type: null, required: false },
  dateRules: { type: null, required: false },
  timeRules: { type: null, required: false },
  dense: { type: Boolean, required: false, default: false },
  pickerOnly: { type: Boolean, required: false, default: false },
  minDate: { type: [Date, String], required: false },
  maxDate: { type: [Date, String], required: false },
  readonly: { type: Boolean, required: false, default: false },
  locale: { type: String, required: false, default: "TH" },
  defaultDate: { type: [Boolean, String], required: false, default: false },
  defaultDateTime: { type: [Boolean, String], required: false, default: false },
  fixedDate: { type: Boolean, required: false, default: false }
});
const emit = defineEmits(["update:modelValue"]);
const attrs = useAttrs();
const plainAttrs = computed(() => {
  return omit(attrs, ["modelValue", "onUpdate:modelValue", "defaultDate", "defaultDateTime", "fixedDate"]);
});
const dateRef = ref();
const timeRef = ref();
const computedRules = computed(() => {
  const isoDateString = Datetime().fromString(`${datePart.value}T${timePart.value}`).toISO();
  if (props.rules && isArray(props.rules)) {
    const ruleConfig = {
      singleModifier: ["DateFuture", "DatetimeFuture", "DateHappen", "DatetimeHappen"],
      twoPartModifier: ["DateAfter", "DateBefore", "DateEqual"]
    };
    const ruleMapping = {
      "DateFuture": useRules().DateFuture,
      "DatetimeFuture": useRules().DatetimeFuture,
      "DateHappen": useRules().DateHappen,
      "DatetimeHappen": useRules().DatetimeHappen,
      "DateAfter": useRules().DateAfter,
      "DateBefore": useRules().DatetimeFuture,
      "DateEqual": useRules().DateEqual
    };
    const applyRule = (ruleName, modifier) => {
      if (props.readonly) return true;
      const ruleFunc = ruleMapping[ruleName](...Object.values(modifier));
      return ruleFunc(isoDateString);
    };
    return props.rules.map((rule) => {
      if (isString(rule)) {
        const ruleParts = rule.split("#");
        const baseRule = ruleParts[0];
        if (ruleConfig.singleModifier.includes(baseRule)) {
          const modifiers = ruleParts.slice(1).join("#").trim() || void 0;
          return applyRule(baseRule, { modifiers });
        }
        if (ruleConfig.twoPartModifier.includes(baseRule)) {
          const [modifier1, ...modifierRest] = ruleParts.slice(1);
          const combinedModifier = modifierRest.join("#") || void 0;
          return applyRule(baseRule, { modifier1, combinedModifier });
        }
      }
      return rule;
    });
  } else {
    return props.rules;
  }
});
const computedDateRules = computed(() => union(computedRules.value, props.dateRules));
const computedTimeRules = computed(() => union(computedRules.value, props.timeRules));
const datePart = ref(null);
const timePart = ref(null);
const pauseEmit = ref(true);
function reset() {
  datePart.value = null;
  timePart.value = null;
  if (props.defaultDateTime) {
    const dateTime = isBoolean(props.defaultDateTime) ? Datetime().now() : Datetime().fromString(props.defaultDateTime);
    if (dateTime.luxonDateTime.isValid) {
      datePart.value = dateTime.toFormat("yyyy-MM-dd");
      timePart.value = dateTime.toFormat("HH:mm:ss");
    }
  } else {
    if (props.defaultDate) {
      if (props.defaultDate === true) {
        datePart.value = Datetime().now().toFormat("yyyy-MM-dd", "EN");
      } else if (typeof props.defaultDate === "string" && /^[+-]?\d+$/.test(props.defaultDate.trim())) {
        datePart.value = Datetime().now().luxonDateTime.plus({ days: Number(props.defaultDate) }).toFormat("yyyy-MM-dd");
      } else {
        const dateTime = Datetime().fromString(props.defaultDate);
        if (dateTime.luxonDateTime.isValid) datePart.value = dateTime.toFormat("yyyy-MM-dd");
      }
    }
  }
}
watchEffect(() => {
  if (!pauseEmit.value) {
    if (datePart.value && timePart.value) {
      emit("update:modelValue", Datetime().fromString(`${datePart.value}T${timePart.value}`).toISO());
    } else if (!datePart.value) {
      emit("update:modelValue", null);
    }
  }
});
const rulesLen = () => props.rules?.length ?? 0;
watch([datePart, timePart, rulesLen], () => {
  if (!props.readonly) nextTick(() => dateRef.value?.validate());
});
watch([datePart, timePart, rulesLen], () => {
  if (!props.readonly) nextTick(() => timeRef.value?.validate());
});
watch(() => props.modelValue, () => {
  pauseEmit.value = true;
  if (!props.modelValue) {
    reset();
    nextTick(() => {
      pauseEmit.value = false;
    });
  } else {
    const dateTime = Datetime().fromString(props.modelValue);
    if (dateTime.luxonDateTime.isValid) {
      datePart.value = dateTime.toFormat("yyyy-MM-dd");
      timePart.value = dateTime.toFormat("HH:mm:ss");
      pauseEmit.value = false;
    } else {
      reset();
      nextTick(() => {
        pauseEmit.value = false;
      });
    }
  }
}, { immediate: true });
</script>

<template>
  <v-container
      :fluid="true"
      class="pa-0"
  >
    <v-row :density="dense ? 'compact' : 'default'">
      <v-col cols="8">
        <FormDate
            v-model="datePart"
            ref="dateRef"
            :rules="computedDateRules"
            :label="label"
            :format="format"
            :picker-only="pickerOnly"
            :readonly="readonly"
            :disabled="fixedDate"
            :min-date="minDate"
            :max-date="maxDate"
            v-bind="plainAttrs"
        />
      </v-col>
      <v-col cols="4">
        <FormTime
            v-model="timePart"
            ref="timeRef"
            :rules="computedTimeRules"
            :label="label"
            :enable-seconds="enableSeconds"
            :picker-only="pickerOnly"
            :readonly="readonly"
            v-bind="plainAttrs"
        />
      </v-col>
    </v-row>
  </v-container>
</template>
