<script setup lang="ts">
/**
 * Props-driven date+time picker composition. DatePicker-style Popover
 * (Calendar) plus two numeric inputs for hour/minute. Round-trips ISO
 * 8601 (e.g. `2024-06-15T13:30:00`). When only the date half is set the
 * value falls back to a date-only ISO string.
 *
 *   <DateTimePicker v-model="when" label="Starts at" />
 */
import { computed, ref, watchEffect } from "vue"
import { Calendar as CalendarIcon, Clock } from "@lucide/vue"
import {
  CalendarDate,
  parseDate,
  type DateValue,
} from "@internationalized/date"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import { Input } from "@/components/ui/input"
import { FieldFrame } from "@/components/ui/field-frame"
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover"
import { cn } from "@/lib/utils"

export interface DateTimePickerProps {
  modelValue?: string | null
  id?: string
  name?: string
  label?: string
  hideLabel?: boolean
  placeholder?: string
  required?: boolean
  readonly?: boolean
  disabled?: boolean
  helpText?: string
  errors?: string[]
}

withDefaults(defineProps<DateTimePickerProps>(), {
  errors: () => [],
})

const model = defineModel<string | null>("modelValue")

const datePart = ref<string>("")
const hourPart = ref<string>("")
const minutePart = ref<string>("")

watchEffect(() => {
  const iso = model.value
  if (!iso) {
    datePart.value = ""
    hourPart.value = ""
    minutePart.value = ""
    return
  }
  const [d, t] = iso.split("T")
  datePart.value = d ?? ""
  if (t) {
    const [hh, mm] = t.split(":")
    hourPart.value = hh ?? ""
    minutePart.value = (mm ?? "").slice(0, 2)
  } else {
    hourPart.value = ""
    minutePart.value = ""
  }
})

const open = ref(false)

const calendarValue = computed<DateValue | undefined>(() => {
  if (!datePart.value) {
    return undefined
  }
  try {
    return parseDate(datePart.value)
  } catch {
    return undefined
  }
})

function emitCombined(): void {
  if (!datePart.value) {
    model.value = null
    return
  }
  if (hourPart.value === "" && minutePart.value === "") {
    model.value = datePart.value
    return
  }
  const hh = padTwo(hourPart.value || "0")
  const mm = padTwo(minutePart.value || "0")
  model.value = `${datePart.value}T${hh}:${mm}:00`
}

function padTwo(value: string): string {
  const n = Math.max(0, parseInt(value, 10) || 0)
  return n.toString().padStart(2, "0")
}

function onDateSelected(value: DateValue | undefined): void {
  if (!value) {
    datePart.value = ""
    emitCombined()
    return
  }
  const calendar =
    value instanceof CalendarDate
      ? value
      : new CalendarDate(value.year, value.month, value.day)
  datePart.value = calendar.toString()
  emitCombined()
  open.value = false
}

function onHourInput(value: string | number | undefined): void {
  const next = String(value ?? "")
  const numeric = Math.min(23, Math.max(0, parseInt(next, 10) || 0))
  hourPart.value = numeric.toString()
  emitCombined()
}

function onMinuteInput(value: string | number | undefined): void {
  const next = String(value ?? "")
  const numeric = Math.min(59, Math.max(0, parseInt(next, 10) || 0))
  minutePart.value = numeric.toString()
  emitCombined()
}

const displayValue = computed<string>(() => {
  if (!datePart.value) {
    return ""
  }
  if (hourPart.value === "" && minutePart.value === "") {
    return datePart.value
  }
  return `${datePart.value} ${padTwo(hourPart.value || "0")}:${padTwo(minutePart.value || "0")}`
})
</script>

<template>
  <FieldFrame
    :label="label"
    :required="required"
    :hide-label="hideLabel"
    :for="id"
    :help-text="helpText"
    :errors="errors"
  >
    <Popover v-model:open="open">
      <PopoverTrigger as-child>
        <Button
          type="button"
          variant="outline"
          :disabled="disabled"
          :class="
            cn(
              'w-full justify-start text-left font-normal',
              !displayValue && 'text-muted-foreground',
            )
          "
          :data-element-name="name ?? undefined"
          :data-readonly="readonly || undefined"
        >
          <CalendarIcon class="mr-2 size-4" />
          <span>{{ displayValue || placeholder || "Pick a date & time" }}</span>
        </Button>
      </PopoverTrigger>
      <PopoverContent class="w-auto p-0">
        <Calendar
          :model-value="calendarValue"
          :placeholder="calendarValue"
          @update:model-value="onDateSelected"
        />
        <div class="flex items-center gap-2 border-t p-3">
          <Clock class="size-4 text-muted-foreground" />
          <Input
            type="number"
            min="0"
            max="23"
            :model-value="hourPart"
            class="w-16"
            :disabled="disabled"
            placeholder="HH"
            data-testid="datetime-hour"
            @update:model-value="onHourInput"
          />
          <span class="text-muted-foreground">:</span>
          <Input
            type="number"
            min="0"
            max="59"
            :model-value="minutePart"
            class="w-16"
            :disabled="disabled"
            placeholder="MM"
            data-testid="datetime-minute"
            @update:model-value="onMinuteInput"
          />
        </div>
      </PopoverContent>
    </Popover>
  </FieldFrame>
</template>
