import dayjs from "dayjs"

export const EVENT_REPEAT_INTERVALS = [
  "Keine Wiederholung",
  "Täglich",
  "Wöchentlich",
  "2-wöchentlich",
  "Monatlich",
  "Jährlich"
] as const

const DEFAULT_REPEAT_INTERVAL = "Keine Wiederholung"
const MAX_OCCURRENCES = 500

const addInterval = (date: dayjs.Dayjs, repeatInterval: string) => {
  switch (repeatInterval) {
    case "Täglich":
      return date.add(1, "day")
    case "Wöchentlich":
      return date.add(1, "week")
    case "2-wöchentlich":
      return date.add(2, "week")
    case "Monatlich":
      return date.add(1, "month")
    case "Jährlich":
      return date.add(1, "year")
    default:
      return null
  }
}

export const normalizeRepeatInterval = (value?: string | null) =>
  EVENT_REPEAT_INTERVALS.includes(value as typeof EVENT_REPEAT_INTERVALS[number])
    ? value as typeof EVENT_REPEAT_INTERVALS[number]
    : DEFAULT_REPEAT_INTERVAL

export const expandRecurringEvent = <T>(
  event: {
    startDate: string | Date
    endDate?: string | Date | null
    repeatInterval?: string | null
  },
  rangeStart: string | Date,
  rangeEnd: string | Date,
  buildOccurrence: (occurrenceStart: dayjs.Dayjs, occurrenceEnd: dayjs.Dayjs | null, occurrenceIndex: number) => T
) => {
  const repeatInterval = normalizeRepeatInterval(event.repeatInterval)
  const baseStart = dayjs(event.startDate)
  const baseEnd = event.endDate ? dayjs(event.endDate) : null
  const visibleStart = dayjs(rangeStart)
  const visibleEnd = dayjs(rangeEnd)
  const durationMs = baseEnd ? Math.max(baseEnd.diff(baseStart), 0) : 0

  if (repeatInterval === DEFAULT_REPEAT_INTERVAL) {
    const occurrenceEnd = baseEnd ? baseStart.add(durationMs, "millisecond") : null
    if (baseStart.isAfter(visibleEnd) || (occurrenceEnd && occurrenceEnd.isBefore(visibleStart))) return []
    return [buildOccurrence(baseStart, occurrenceEnd, 0)]
  }

  const occurrences: T[] = []
  let occurrenceStart = baseStart
  let occurrenceIndex = 0

  while (occurrenceIndex < MAX_OCCURRENCES && occurrenceStart.isBefore(visibleEnd.add(1, "day"))) {
    const occurrenceEnd = baseEnd ? occurrenceStart.add(durationMs, "millisecond") : null

    if (!occurrenceStart.isAfter(visibleEnd) && !(occurrenceEnd && occurrenceEnd.isBefore(visibleStart))) {
      occurrences.push(buildOccurrence(occurrenceStart, occurrenceEnd, occurrenceIndex))
    }

    const nextStart = addInterval(occurrenceStart, repeatInterval)
    if (!nextStart || nextStart.isSame(occurrenceStart)) break
    occurrenceStart = nextStart
    occurrenceIndex += 1
  }

  return occurrences
}
