<script setup lang="ts">
import { defineModel, defineProps, computed, ref } from "vue";

const props = defineProps<{
  rules?: ((value: string) => boolean | string)[]; // Accepts external validation rules
}>();

const dateValue = defineModel<string>({ default: "" });
const inputRef = ref<HTMLInputElement | null>(null);

const formatDate = (event: Event) => {
  const input = event.target as HTMLInputElement;
  let value = input.value.replace(/\D/g, ""); // Remove non-numeric characters

  // Format as MM/DD/YYYY
  let formattedValue = value
    .slice(0, 8)
    .replace(/(\d{2})(\d{0,2})?(\d{0,4})?/, (_, m, d, y) =>
      [m, d, y].filter(Boolean).join("/")
    );

  // Preserve cursor position
  const cursorPosition = input.selectionStart ?? 0;
  const slashCountBefore = (dateValue.value.match(/\//g) || []).length;
  const slashCountAfter = (formattedValue.match(/\//g) || []).length;
  const cursorOffset = slashCountAfter - slashCountBefore;

  // Only update if value changed to prevent unnecessary reactivity updates
  if (dateValue.value !== formattedValue) {
    dateValue.value = formattedValue;
    setTimeout(() => {
      input.setSelectionRange(
        cursorPosition + cursorOffset,
        cursorPosition + cursorOffset
      );
    });
  }
};

// Compute combined validation rules
const computedRules = computed(() => {
  return props.rules ? [...props.rules] : [];
});

// Handle arrow key increments with cursor preservation
const handleArrowKeys = (event: KeyboardEvent) => {
  if (!dateValue.value) return;

  const input = event.target as HTMLInputElement;
  const cursorPosition = input.selectionStart ?? 0; // Store cursor position
  dateValue.value.split("/").map(Number);

  let updatedDate = dateValue.value;

  // Determine which part to modify
  if (cursorPosition <= 2) {
    updatedDate = modifyDatePart(
      dateValue.value,
      "month",
      event.key === "ArrowUp" ? 1 : -1
    );
  } else if (cursorPosition <= 5) {
    updatedDate = modifyDatePart(
      dateValue.value,
      "day",
      event.key === "ArrowUp" ? 1 : -1
    );
  } else {
    updatedDate = modifyDatePart(
      dateValue.value,
      "year",
      event.key === "ArrowUp" ? 1 : -1
    );
  }

  if (dateValue.value !== updatedDate) {
    dateValue.value = updatedDate;
    setTimeout(() => {
      input.setSelectionRange(cursorPosition, cursorPosition);
    });
  }

  event.preventDefault();
};

const modifyDatePart = (
  date: string,
  part: "month" | "day" | "year",
  change: number
) => {
  let [month, day, year] = date.split("/").map(Number);

  if (part === "month") {
    month = Math.max(1, Math.min(12, month + change));
    const maxDays = new Date(year, month, 0).getDate();
    day = Math.min(day, maxDays); // Adjust day to fit new month's max days
  } else if (part === "day") {
    const maxDays = new Date(year, month, 0).getDate();
    day = Math.max(1, Math.min(maxDays, day + change));
  } else if (part === "year") {
    year += change;
    const maxDays = new Date(year, month, 0).getDate();
    day = Math.min(day, maxDays); // Adjust day to fit new year's month
  }

  return `${String(month).padStart(2, "0")}/${String(day).padStart(
    2,
    "0"
  )}/${year}`;
};
</script>

<template>
  <v-text-field
    ref="inputRef"
    v-model="dateValue"
    placeholder="MM/DD/YYYY"
    @input="formatDate"
    @keydown.up="handleArrowKeys"
    @keydown.down="handleArrowKeys"
    :rules="computedRules"
  ></v-text-field>
</template>
