<template>
  <div
    data-test="month-grid"
    @mouseleave="emit('hover', null)"
  >
    <div class="grid grid-cols-[auto_repeat(7,minmax(0,1fr))]">
      <div class="mr-[12px] flex h-8 w-[35px] items-center justify-center text-[14px] font-medium opacity-[60%]">
        S
      </div>
      <div
        v-for="d in dayLabels"
        :key="d"
        class="flex h-8 items-center justify-center text-[14px] font-medium opacity-[60%]"
      >
        {{ d }}
      </div>

      <template
        v-for="(week, wIndex) in weeks"
        :key="week.days[0].isoDate"
      >
        <component
          :is="interactiveWeekNumber ? 'button' : 'div'"
          data-test="week-number"
          :data-week-start="week.days[0].isoDate"
          :data-marked="markedWeekStart === week.days[0].isoDate"
          :type="interactiveWeekNumber ? 'button' : undefined"
          :disabled="interactiveWeekNumber ? !weekSelectable(week) : undefined"
          class="mr-[12px] flex h-[45px] w-[35px] shrink-0 items-center justify-center p-[10px] text-sm"
          :class="[
            weekNumberClass(week),
            wIndex === 0 ? 'rounded-t-md' : '',
            wIndex === weeks.length - 1 ? 'rounded-b-md' : '',
          ]"
          @click="onWeekNumberClick(week)"
          @mouseenter="onWeekNumberHover(week)"
        >
          {{ week.weekNumber }}
        </component>
        <button
          v-for="cell in week.days"
          :key="cell.isoDate"
          type="button"
          data-test="day"
          :data-iso="cell.isoDate"
          :data-range-role="roleOf(cell)"
          :disabled="!inRange(cell.isoDate)"
          :aria-label="ariaLabel(cell)"
          :aria-disabled="!inRange(cell.isoDate)"
          class="relative flex h-[45px] w-full items-center justify-center"
          :class="inRange(cell.isoDate) ? 'cursor-pointer' : 'cursor-not-allowed'"
          @click="onSelect(cell.isoDate)"
          @mouseenter="emit('hover', cell.isoDate)"
        >
          <span
            v-if="roleOf(cell) === 'in-range'"
            class="absolute inset-x-0 top-1/2 h-10 -translate-y-1/2 bg-m-primary-light"
          />
          <span
            v-else-if="roleOf(cell) === 'start'"
            class="absolute inset-x-0 top-1/2 h-10 -translate-y-1/2 rounded-l-full bg-m-primary-light"
          />
          <span
            v-else-if="roleOf(cell) === 'end'"
            class="absolute inset-x-0 top-1/2 h-10 -translate-y-1/2 rounded-r-full bg-m-primary-light"
          />
          <span
            class="relative flex h-10 w-10 items-center justify-center rounded-full text-sm font-medium transition-colors duration-100"
            :class="cellClass(cell)"
          >
            {{ cell.day }}
          </span>
        </button>
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
import {computed, toRef} from 'vue'
import {useMonthMatrix, type DayCell, type WeekRow} from '../composables/useMonthMatrix'
import {isDateInRange} from '../composables/dateFormat'
import {dayRangeRole, resolveRangeBounds, type DayRangeRole} from '../composables/dateRange'

defineOptions({name: 'MalioDateMonthGrid'})

const props = withDefaults(
  defineProps<{
    month: number
    year: number
    selectedDate?: string | null
    rangeStart?: string | null
    rangeEnd?: string | null
    previewDate?: string | null
    interactiveWeekNumber?: boolean
    markedWeekStart?: string | null
    min?: string
    max?: string
  }>(),
  {
    selectedDate: null,
    rangeStart: undefined,
    rangeEnd: undefined,
    previewDate: undefined,
    interactiveWeekNumber: false,
    markedWeekStart: null,
    min: undefined,
    max: undefined,
  },
)

const emit = defineEmits<{
  (e: 'select', iso: string): void
  (e: 'hover', iso: string | null): void
}>()

const dayLabels = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim']
const monthsLong = ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',
  'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre']

const {weeks} = useMonthMatrix(toRef(props, 'month'), toRef(props, 'year'))

const inRange = (iso: string) => isDateInRange(iso, props.min, props.max)

const weekSelectable = (week: WeekRow) => week.days.some(d => inRange(d.isoDate))

const weekNumberClass = (week: WeekRow) => {
  if (props.markedWeekStart === week.days[0].isoDate) return 'bg-m-primary text-white'
  const parts = ['bg-m-primary-light']
  parts.push(week.days.some(d => d.isToday) ? 'text-black' : 'text-black/60')
  if (props.interactiveWeekNumber && weekSelectable(week)) parts.push('cursor-pointer')
  return parts.join(' ')
}

const onWeekNumberClick = (week: WeekRow) => {
  if (!props.interactiveWeekNumber || !weekSelectable(week)) return
  emit('select', week.days[0].isoDate)
}

const onWeekNumberHover = (week: WeekRow) => {
  if (!props.interactiveWeekNumber || !weekSelectable(week)) return
  emit('hover', week.days[0].isoDate)
}

const isRangeMode = computed(() => props.rangeStart !== undefined)
const bounds = computed(() =>
  isRangeMode.value
    ? resolveRangeBounds(props.rangeStart ?? null, props.rangeEnd ?? null, props.previewDate ?? null)
    : null,
)

const roleOf = (cell: DayCell): DayRangeRole => {
  if (isRangeMode.value) return dayRangeRole(cell.isoDate, bounds.value)
  return props.selectedDate === cell.isoDate ? 'single' : 'none'
}

const ariaLabel = (cell: DayCell) => {
  const [, m, d] = cell.isoDate.split('-')
  return `${Number(d)} ${monthsLong[Number(m) - 1]} ${cell.isoDate.slice(0, 4)}`
}

const cellClass = (cell: DayCell) => {
  if (!inRange(cell.isoDate)) return 'text-m-muted/30'
  const role = roleOf(cell)
  if (role === 'start' || role === 'end' || role === 'single') return 'bg-m-primary text-white'
  if (role === 'in-range') return 'text-black'
  const parts = ['hover:bg-m-primary/10']
  if (cell.isToday) parts.push('border border-m-primary text-m-primary')
  else if (cell.isCurrentMonth) parts.push('text-black')
  else parts.push('opacity-[60%]')
  return parts.join(' ')
}

const onSelect = (iso: string) => {
  if (!inRange(iso)) return
  emit('select', iso)
}
</script>
