<script setup lang="ts">
import { computed, ref } from 'vue'
import type { EventOccurrence } from '@ChronoManager/types/event'
import { formatDisplayDate, formatLocalIsoDate } from '@/utils/format'

type Frequency = 'daily' | 'weekly' | 'monthly' | 'yearly'
type EndMode = 'never' | 'date' | 'count'
type RecurrencePattern = EventOccurrence & { toJson?: () => EventOccurrence }

interface Props {
  mode: 'edit' | 'view'
  pattern: RecurrencePattern | null
  startsOn?: string | null
}

const props = defineProps<Props>()

const emit = defineEmits<{
  'update:pattern': [pattern: EventOccurrence | null]
}>()

const precisionOptions: { title: string, value: Frequency }[] = [
  { title: 'Daily', value: 'daily' },
  { title: 'Weekly', value: 'weekly' },
  { title: 'Monthly', value: 'monthly' },
  { title: 'Yearly', value: 'yearly' },
]

const weekdayOptions = [
  { title: 'Monday', short: 'Mon', value: 1 },
  { title: 'Tuesday', short: 'Tue', value: 2 },
  { title: 'Wednesday', short: 'Wed', value: 3 },
  { title: 'Thursday', short: 'Thu', value: 4 },
  { title: 'Friday', short: 'Fri', value: 5 },
  { title: 'Saturday', short: 'Sat', value: 6 },
  { title: 'Sunday', short: 'Sun', value: 7 },
]

const monthOptions = [
  'January', 'February', 'March', 'April', 'May', 'June',
  'July', 'August', 'September', 'October', 'November', 'December',
].map((title, index) => ({ title, value: index + 1 }))

const monthDayOptions = Array.from({ length: 31 }, (_, index) => index + 1)

const positionOptions = [
  { title: 'First', value: 1 },
  { title: 'Second', value: 2 },
  { title: 'Third', value: 3 },
  { title: 'Fourth', value: 4 },
  { title: 'Last', value: -1 },
]

const frequencyUnits: Record<Frequency, [string, string]> = {
  daily: ['day', 'days'],
  weekly: ['week', 'weeks'],
  monthly: ['month', 'months'],
  yearly: ['year', 'years'],
}

const endOptions: { title: string, value: EndMode }[] = [
  { title: 'Never', value: 'never' },
  { title: 'On', value: 'date' },
  { title: 'After', value: 'count' },
]

const endDateMenu = ref(false)

const recurrenceRuleFields: (keyof EventOccurrence)[] = [
  'onDayOfWeek', 'onDayOfMonth', 'onDayOfYear', 'onWeekOfMonth',
  'onWeekOfYear', 'onMonthOfYear', 'onHour', 'onMinute', 'onSecond', 'onPosition',
]

const toPlainPattern = (): EventOccurrence | null => {
  if (!props.pattern) return null
  return props.pattern.toJson ? props.pattern.toJson() : { ...props.pattern }
}

const updatePattern = (changes: Partial<EventOccurrence>) => {
  const current = toPlainPattern() ?? { pattern: 'absolute', precision: 'daily', interval: 1 }
  emit('update:pattern', { ...current, ...changes })
}

const getStartDate = () => {
  const date = props.startsOn ? new Date(props.startsOn) : new Date()
  return Number.isNaN(date.getTime()) ? new Date() : date
}

const getDateDefaults = () => {
  const date = getStartDate()
  const day = date.getDate()
  const weekday = date.getDay() || 7
  const daysInMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
  const position = day + 7 > daysInMonth ? -1 : Math.ceil(day / 7)
  return { day, month: date.getMonth() + 1, weekday, position }
}

const repeatValue = computed<Frequency>(() => props.pattern?.precision as Frequency ?? 'daily')
const endMode = computed<EndMode>(() => {
  if (props.pattern?.iterations) return 'count'
  if (props.pattern?.concludes) return 'date'
  return 'never'
})

const intervalUnit = computed(() => {
  const frequency = props.pattern?.precision as Frequency ?? 'daily'
  const interval = props.pattern?.interval || 1
  const units = frequencyUnits[frequency] ?? ['period', 'periods']
  return units[interval === 1 ? 0 : 1]
})

const changeFrequency = (frequency: Frequency) => {
  const defaults = getDateDefaults()
  const changes: Partial<EventOccurrence> = { precision: frequency, pattern: 'absolute' }
  for (const field of recurrenceRuleFields) {
    ;(changes as Record<string, unknown>)[field] = null
  }

  if (frequency === 'weekly') changes.onDayOfWeek = [defaults.weekday]
  if (frequency === 'monthly') changes.onDayOfMonth = [defaults.day]
  if (frequency === 'yearly') {
    changes.onMonthOfYear = [defaults.month]
    changes.onDayOfMonth = [defaults.day]
  }
  updatePattern(changes)
}

const addRecurrence = () => {
  emit('update:pattern', { pattern: 'absolute', precision: 'daily', interval: 1 })
}

const changePatternType = (patternType: 'absolute' | 'relative') => {
  const defaults = getDateDefaults()
  const yearly = props.pattern?.precision === 'yearly'
  updatePattern(patternType === 'absolute' ? {
    pattern: 'absolute',
    onDayOfWeek: null,
    onDayOfMonth: props.pattern?.onDayOfMonth?.length ? props.pattern.onDayOfMonth : [defaults.day],
    onMonthOfYear: yearly
      ? (props.pattern?.onMonthOfYear?.length ? props.pattern.onMonthOfYear : [defaults.month])
      : null,
    onPosition: null,
  } : {
    pattern: 'relative',
    onDayOfWeek: props.pattern?.onDayOfWeek?.length ? props.pattern.onDayOfWeek : [defaults.weekday],
    onDayOfMonth: null,
    onMonthOfYear: yearly
      ? (props.pattern?.onMonthOfYear?.length ? props.pattern.onMonthOfYear : [defaults.month])
      : null,
    onPosition: props.pattern?.onPosition?.length ? props.pattern.onPosition : [defaults.position],
  })
}

const updatePosition = (position: number | null) => {
  updatePattern({ onPosition: position === null ? [] : [position] })
}

const updateEndMode = (mode: EndMode) => {
  if (mode === 'never') {
    updatePattern({ iterations: null, concludes: null })
  } else if (mode === 'count') {
    updatePattern({ iterations: props.pattern?.iterations || 10, concludes: null })
  } else {
    const conclusion = new Date(getStartDate())
    conclusion.setMonth(conclusion.getMonth() + 1)
    updatePattern({ iterations: null, concludes: props.pattern?.concludes || conclusion.toISOString().slice(0, 10) })
  }
}

const pickerDate = (value: string | null | undefined): Date | null => {
  return value ? new Date(`${value.slice(0, 10)}T00:00:00`) : null
}

const selectConclusionDate = (value: unknown) => {
  const selected = Array.isArray(value) ? value[0] : value
  const date = selected instanceof Date ? selected : new Date(String(selected))
  if (!Number.isNaN(date.getTime())) {
    updatePattern({ concludes: formatLocalIsoDate(date), iterations: null })
  }
  endDateMenu.value = false
}

const weekdayName = (day: number) => weekdayOptions.find(option => option.value === day)?.short ?? String(day)
const monthName = (month: number) => monthOptions.find(option => option.value === month)?.title ?? String(month)

const formatRecurrence = (pattern: RecurrencePattern) => {
  const interval = pattern.interval || 1
  const frequency = pattern.precision
  const units = frequencyUnits[frequency as Frequency] ?? [frequency, frequency]
  const unit = units[interval === 1 ? 0 : 1]
  const parts = [`Every ${interval > 1 ? `${interval} ` : ''}${unit}`]

  if (frequency === 'weekly' && pattern.onDayOfWeek?.length) {
    parts.push(`on ${pattern.onDayOfWeek.map(weekdayName).join(', ')}`)
  }

  if (frequency === 'monthly' || frequency === 'yearly') {
    if (frequency === 'yearly' && pattern.onMonthOfYear?.length) {
      parts.push(`in ${pattern.onMonthOfYear.map(monthName).join(', ')}`)
    }
    if (pattern.pattern === 'absolute' && pattern.onDayOfMonth?.length) {
      parts.push(`on day ${pattern.onDayOfMonth.join(', ')}`)
    } else if (pattern.onPosition?.length && pattern.onDayOfWeek?.length) {
      const position = positionOptions.find(option => option.value === pattern.onPosition?.[0])?.title
      parts.push(`on the ${position?.toLowerCase()} ${pattern.onDayOfWeek.map(weekdayName).join(', ')}`)
    }
  }

  if (pattern.iterations) {
    parts.push(`for ${pattern.iterations} occurrences`)
  } else if (pattern.concludes) {
    parts.push(`until ${new Date(`${pattern.concludes.slice(0, 10)}T00:00:00`).toLocaleDateString()}`)
  }
  return parts.join(' ')
}
</script>

<template>
  <div v-if="pattern || mode === 'edit'" class="event-editor-section">
    <div class="d-flex align-center justify-space-between mb-2">
      <div class="text-subtitle-1">Recurrence</div>
      <v-btn
        v-if="mode === 'edit' && !pattern"
        icon="mdi-repeat"
        size="x-small"
        variant="text"
        rounded="0"
        title="Add Recurrence"
        @click="addRecurrence"
      />
      <v-btn
        v-else-if="mode === 'edit'"
        icon="mdi-delete"
        aria-label="Remove recurrence"
        size="small"
        color="error"
        variant="text"
        @click="$emit('update:pattern', null)"
      />
    </div>

    <div v-if="mode === 'view' && pattern" class="mb-4">
      <div class="recurrence-summary pa-3 rounded">
        <v-icon icon="mdi-repeat" size="small" class="mr-2" />
        <strong>{{ formatRecurrence(pattern) }}</strong>
      </div>
    </div>

    <div v-else-if="mode === 'edit' && pattern" class="recurrence-editor pa-3 rounded">
      <div class="recurrence-controls mb-4">
        <span>Repeat</span>
        <v-select
          :model-value="repeatValue"
          :items="precisionOptions"
          variant="outlined"
          density="compact"
          hide-details
          class="frequency-field"
          @update:model-value="changeFrequency"
        />
        <template v-if="pattern">
          <span>Every</span>
          <v-text-field
            :model-value="pattern.interval"
            aria-label="Repeat interval"
            type="number"
            min="1"
            max="999"
            variant="outlined"
            density="compact"
            hide-details
            class="interval-field"
            @update:model-value="updatePattern({ interval: Math.max(1, Number($event) || 1) })"
          />
          <span>{{ intervalUnit }}</span>
        </template>
      </div>

      <v-expand-transition>
        <div v-if="pattern" class="mt-4">
          <div v-if="pattern.precision === 'weekly'" class="weekly-on-controls mb-4">
            <span>On days</span>
            <v-btn-toggle
              :model-value="pattern.onDayOfWeek || []"
              color="primary"
              multiple
              mandatory
              divided
              variant="outlined"
              class="weekday-toggle"
              @update:model-value="updatePattern({ onDayOfWeek: $event })"
            >
              <v-btn
                v-for="day in weekdayOptions"
                :key="day.value"
                :value="day.value"
                size="small"
                variant="outlined"
              >
                {{ day.short.slice(0, 2) }}
                <v-tooltip activator="parent" location="top">{{ day.title }}</v-tooltip>
              </v-btn>
            </v-btn-toggle>
          </div>

          <div v-if="pattern.precision === 'monthly'" class="monthly-rule-controls mb-4">
            <span>Using</span>
            <v-btn-toggle
              :model-value="pattern.pattern"
              color="primary"
              mandatory
              divided
              variant="outlined"
              class="rule-toggle"
              @update:model-value="changePatternType"
            >
              <v-btn value="absolute">Calendar date</v-btn>
              <v-btn value="relative">Weekday pattern</v-btn>
            </v-btn-toggle>

            <v-select
              v-if="pattern.pattern === 'absolute'"
              :model-value="pattern.onDayOfMonth?.[0] ?? null"
              :items="monthDayOptions"
              label="Day"
              variant="outlined"
              density="compact"
              hide-details
              class="monthly-day-field"
              @update:model-value="updatePattern({ onDayOfMonth: [$event] })"
            />

            <template v-else>
              <v-select
                :model-value="pattern.onPosition?.[0] ?? null"
                :items="positionOptions"
                label="Which"
                variant="outlined"
                density="compact"
                hide-details
                class="monthly-position-field"
                @update:model-value="updatePosition"
              />
              <v-select
                :model-value="pattern.onDayOfWeek?.[0] ?? null"
                :items="weekdayOptions"
                label="Weekday"
                variant="outlined"
                density="compact"
                hide-details
                class="monthly-weekday-field"
                @update:model-value="updatePattern({ onDayOfWeek: [$event] })"
              />
            </template>
          </div>

          <div v-else-if="pattern.precision === 'yearly'" class="yearly-rule-controls mb-4">
            <span>Using</span>
            <v-btn-toggle
              :model-value="pattern.pattern"
              color="primary"
              mandatory
              divided
              variant="outlined"
              class="rule-toggle"
              @update:model-value="changePatternType"
            >
              <v-btn value="absolute">Calendar date</v-btn>
              <v-btn value="relative">Weekday pattern</v-btn>
            </v-btn-toggle>

            <v-select
              :model-value="pattern.onMonthOfYear?.[0] ?? null"
              :items="monthOptions"
              label="Month"
              variant="outlined"
              density="compact"
              hide-details
              class="yearly-month-field"
              @update:model-value="updatePattern({ onMonthOfYear: [$event] })"
            />

            <v-select
              v-if="pattern.pattern === 'absolute'"
              :model-value="pattern.onDayOfMonth?.[0] ?? null"
              :items="monthDayOptions"
              label="Day"
              variant="outlined"
              density="compact"
              hide-details
              class="monthly-day-field"
              @update:model-value="updatePattern({ onDayOfMonth: [$event] })"
            />

            <template v-else>
              <v-select
                :model-value="pattern.onPosition?.[0] ?? null"
                :items="positionOptions"
                label="Which"
                variant="outlined"
                density="compact"
                hide-details
                class="monthly-position-field"
                @update:model-value="updatePosition"
              />
              <v-select
                :model-value="pattern.onDayOfWeek?.[0] ?? null"
                :items="weekdayOptions"
                label="Weekday"
                variant="outlined"
                density="compact"
                hide-details
                class="monthly-weekday-field"
                @update:model-value="updatePattern({ onDayOfWeek: [$event] })"
              />
            </template>
          </div>

          <div class="recurrence-end-controls mb-4">
            <span>Ends</span>
            <v-select
              :model-value="endMode"
              :items="endOptions"
              aria-label="Recurrence end"
              variant="outlined"
              density="compact"
              hide-details
              class="end-mode-field"
              @update:model-value="updateEndMode"
            />

            <v-menu
              v-if="endMode === 'date'"
              v-model="endDateMenu"
              :close-on-content-click="false"
              location="bottom start"
            >
              <template #activator="{ props: activatorProps }">
                <v-text-field
                  v-bind="activatorProps"
                  :model-value="formatDisplayDate(pattern.concludes)"
                  aria-label="End date"
                  prepend-inner-icon="mdi-calendar"
                  variant="outlined"
                  density="compact"
                  hide-details
                  readonly
                  class="end-date-field"
                />
              </template>
              <v-date-picker
                :model-value="pickerDate(pattern.concludes)"
                :min="pickerDate(startsOn)"
                color="primary"
                show-adjacent-months
                @update:model-value="selectConclusionDate"
              />
            </v-menu>

            <v-text-field
              v-else-if="endMode === 'count'"
              :model-value="pattern.iterations"
              aria-label="Number of occurrences"
              type="number"
              min="1"
              variant="outlined"
              density="compact"
              hide-details
              class="interval-field"
              @update:model-value="updatePattern({ iterations: Math.max(1, Number($event) || 1), concludes: null })"
            />
            <span v-if="endMode === 'count'">occurrences</span>
          </div>

          <div class="recurrence-summary pa-3 rounded">
            <v-icon icon="mdi-repeat" size="small" class="mr-2" />
            {{ formatRecurrence(pattern) }}
          </div>
        </div>
      </v-expand-transition>
    </div>
  </div>
</template>

<style scoped>
.event-editor-section {
  margin-bottom: 1.5rem;
}

.recurrence-editor {
  border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
}

.recurrence-summary {
  background: rgba(var(--v-theme-primary), 0.08);
  color: rgb(var(--v-theme-on-surface));
}

.recurrence-controls,
.recurrence-end-controls,
.weekly-on-controls,
.monthly-rule-controls,
.yearly-rule-controls,
.recurrence-sentence {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.recurrence-controls,
.recurrence-end-controls,
.weekly-on-controls,
.monthly-rule-controls,
.yearly-rule-controls {
  flex-wrap: nowrap;
}

.frequency-field {
  flex: 0 1 9.5rem;
  min-width: 0;
  max-width: 9.5rem;
}

.interval-field {
  flex: 0 0 5rem;
}

.end-mode-field {
  flex: 0 0 7rem;
  max-width: 7rem;
}

.end-date-field {
  flex: 0 1 12rem;
  min-width: 0;
  max-width: 12rem;
}

.field-label {
  color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
  font-size: 0.875rem;
  font-weight: 500;
}

.weekday-toggle,
.rule-toggle {
  display: flex;
  flex-wrap: wrap;
  height: auto;
}

.weekday-toggle :deep(.v-btn) {
  flex: 1 1 2.5rem;
  min-width: 2.5rem;
  min-height: 40px;
  border: 1px solid rgba(var(--v-theme-on-surface), 0.35) !important;
}

.weekday-toggle :deep(.v-btn--active) {
  border-color: rgb(var(--v-theme-primary)) !important;
}

.weekly-on-controls .weekday-toggle {
  flex: 1 1 auto;
  flex-wrap: nowrap;
  min-width: 0;
}

.monthly-rule-controls .rule-toggle,
.yearly-rule-controls .rule-toggle {
  flex: 0 0 auto;
  flex-wrap: nowrap;
}

.monthly-rule-controls .rule-toggle :deep(.v-btn),
.yearly-rule-controls .rule-toggle :deep(.v-btn) {
  min-height: 40px;
}

.monthly-day-field {
  flex: 0 0 6rem;
  max-width: 6rem;
}

.monthly-position-field {
  flex: 0 0 6.5rem;
  max-width: 6.5rem;
}

.monthly-weekday-field {
  flex: 0 1 8.5rem;
  min-width: 0;
  max-width: 8.5rem;
}

.yearly-month-field {
  flex: 0 1 8rem;
  min-width: 0;
  max-width: 8rem;
}

@media (max-width: 599px) {
  .rule-toggle :deep(.v-btn) {
    flex: 1 1 auto;
  }
}
</style>
