<template>
  <CalendarField
    :id="id"
    :display-value="displayValue"
    :sync-to="datePart"
    :name="name"
    :label="label"
    :placeholder="placeholder"
    :required="required"
    :disabled="disabled"
    :readonly="readonly"
    :hint="hint"
    :error="mergedError"
    :success="success"
    :clearable="clearable"
    :editable="editable"
    placeholder-template="JJ/MM/AAAA HH:MM"
    :input-class="inputClass"
    :label-class="labelClass"
    :group-class="groupClass"
    v-bind="$attrs"
    @clear="onClear"
    @commit="onCommit"
  >
    <template #default="{ currentMonth, currentYear }">
      <MonthGrid
        :month="currentMonth"
        :year="currentYear"
        :selected-date="datePart"
        :min="min?.slice(0, 10)"
        :max="max?.slice(0, 10)"
        @select="onSelectDay"
      />
      <div class="mt-4">
        <MalioTimePicker
          :model-value="timeValue || null"
          label="Heure"
          :clearable="false"
          static-popover
          @update:model-value="onTimeChange"
        />
      </div>
    </template>
  </CalendarField>
</template>

<script setup lang="ts">
import {computed, ref, watch} from 'vue'
import CalendarField from './internal/CalendarField.vue'
import MonthGrid from './internal/MonthGrid.vue'
import MalioTimePicker from '../time/TimePicker.vue'
import {formatTime} from '../time/composables/timeFormat'
import {isDateInRange} from './composables/dateFormat'
import {composeDateTime, formatIsoDateTimeToDisplay, isValidIsoDateTime, parseDisplayToIsoDateTime, splitDateTime} from './composables/datetimeFormat'

defineOptions({name: 'MalioDateTime', inheritAttrs: false})

const props = withDefaults(
  defineProps<{
    id?: string
    name?: string
    label?: string
    modelValue?: string | null
    placeholder?: string
    required?: boolean
    disabled?: boolean
    readonly?: boolean
    hint?: string
    error?: string
    success?: string
    min?: string
    max?: string
    clearable?: boolean
    editable?: boolean
    invalidMessage?: string
    inputClass?: string
    labelClass?: string
    groupClass?: string
  }>(),
  {
    id: '',
    name: '',
    label: '',
    modelValue: undefined,
    placeholder: 'JJ/MM/AAAA HH:MM',
    required: false,
    disabled: false,
    readonly: false,
    hint: '',
    error: '',
    success: '',
    min: undefined,
    max: undefined,
    clearable: true,
    editable: false,
    invalidMessage: 'Date invalide',
    inputClass: '',
    labelClass: '',
    groupClass: '',
  },
)

const emit = defineEmits<{
  (e: 'update:modelValue', value: string | null): void
  (e: 'update:valid', value: boolean): void
  // Canal séparé pour la saisie invalide (validation back-autoritative) : texte brut
  // tel que tapé sur saisie non parsable/hors plage, '' sinon. Ne JAMAIS transiter
  // par modelValue, qui doit rester ISO|null pour l'affichage et le round-trip.
  (e: 'update:rawValue', value: string): void
}>()

// pendingTime : heure réglée avant qu'un jour ne soit choisi (sinon on ne peut pas émettre).
const pendingTime = ref('')

const parts = computed(() => splitDateTime(props.modelValue ?? null))
const datePart = computed(() => parts.value.date)
const displayValue = computed(() => formatIsoDateTimeToDisplay(props.modelValue ?? null))
const timeValue = computed(() => parts.value.time || pendingTime.value)

const internalError = ref('')
const mergedError = computed(() => props.error || internalError.value)

// La validité ne reflète que la saisie clavier : malformée/hors plage → false. Un
// champ vide est valide (l'obligation `required` reste à la charge du parent).
const setError = (message: string) => {
  internalError.value = message
  emit('update:valid', message === '')
}

function onSelectDay(iso: string) {
  // Si aucune heure n'a été choisie, on prend l'heure actuelle (pas 00:00).
  // (heure courante au moment du clic)
  const now = new Date()
  const time = parts.value.time || pendingTime.value || formatTime(now.getHours(), now.getMinutes())
  setError('')
  emit('update:rawValue', '')
  emit('update:modelValue', composeDateTime(iso, time))
}

function onTimeChange(value: string | null) {
  if (!value) return
  if (datePart.value) {
    setError('')
    emit('update:rawValue', '')
    emit('update:modelValue', composeDateTime(datePart.value, value))
  }
  else {
    pendingTime.value = value
  }
}

function onCommit(text: string) {
  const trimmed = text.trim()
  if (trimmed === '') {
    setError('')
    emit('update:rawValue', '')
    emit('update:modelValue', null)
    return
  }
  const iso = parseDisplayToIsoDateTime(trimmed)
  if (iso && isDateInRange(iso, props.min, props.max)) {
    setError('')
    emit('update:rawValue', '')
    emit('update:modelValue', iso)
    return
  }
  setError(props.invalidMessage)
  emit('update:rawValue', trimmed)
}

function onClear() {
  setError('')
  pendingTime.value = ''
  emit('update:rawValue', '')
  emit('update:modelValue', null)
}

// immediate : émet aussi la validité au montage, pour que le parent connaisse
// l'état d'un champ pré-rempli (formulaire d'édition) sans interaction préalable.
watch(() => props.modelValue, (val) => {
  setError('')
  if (val && !isValidIsoDateTime(val) && import.meta.dev) {
    console.warn(`[MalioDateTime] modelValue invalide ignoré : "${val}"`)
  }
}, {immediate: true})
</script>
