import { addDays } from "date-fns"
import { parseICSDate } from "./agenda-date.ts"
import {
  fallbackCalendarColor,
  normalizeAgendaColor,
} from "./agenda-colors.ts"
import { expandOccurrences, parseRRule } from "./agenda-recurrence.ts"
import type {
  AgendaApiEvent,
  AgendaCalendar,
  AgendaEvent,
} from "./agenda-types.ts"

export function calendarColor(calendar: AgendaCalendar): string {
  return normalizeAgendaColor(calendar.color) || fallbackCalendarColor(calendar.id)
}

/**
 * Transforme les événements API d'un agenda en occurrences affichables,
 * en développant les récurrences dans la fenêtre demandée.
 */
export function expandApiEvents(
  calendar: AgendaCalendar,
  events: AgendaApiEvent[],
  rangeStart: Date,
  rangeEnd: Date,
): AgendaEvent[] {
  const baseColor = calendarColor(calendar)
  const out: AgendaEvent[] = []

  for (const api of events) {
    const start = parseICSDate(api.start)
    if (!start) continue
    let end = api.end ? parseICSDate(api.end) : null
    if (!end || end <= start) {
      end = api.all_day ? addDays(start, 1) : new Date(start.getTime() + 30 * 60000)
    }
    const durationMs = end.getTime() - start.getTime()
    const color = normalizeAgendaColor(api.color) || baseColor

    const base: Omit<AgendaEvent, "key" | "start" | "end"> = {
      calendarId: calendar.id,
      path: api.path ?? "",
      etag: api.etag ?? "",
      uid: api.uid,
      title: api.summary || "(Sans titre)",
      description: api.description ?? "",
      location: api.location ?? "",
      meetUrl: api.meet_url ?? "",
      organizer: api.organizer ?? "",
      attendees: api.attendees ?? [],
      allDay: api.all_day,
      color,
      rrule: api.rrule ?? "",
      recurring: Boolean(api.rrule),
      master: api,
    }

    const rule = api.rrule ? parseRRule(api.rrule) : null
    if (!rule) {
      if (end >= rangeStart && start <= rangeEnd) {
        out.push({ ...base, key: `${base.path}@${start.getTime()}`, start, end })
      }
      continue
    }

    const exdates = new Set<number>(
      (api.exdates ?? [])
        .map((ex) => parseICSDate(ex)?.getTime())
        .filter((t): t is number => typeof t === "number"),
    )
    // Étend la fenêtre vers l'amont pour capter les occurrences qui débordent.
    const occurrenceWindowStart = new Date(rangeStart.getTime() - durationMs)
    for (const occStart of expandOccurrences(
      start,
      rule,
      exdates,
      occurrenceWindowStart,
      rangeEnd,
    )) {
      const occEnd = new Date(occStart.getTime() + durationMs)
      out.push({
        ...base,
        key: `${base.path}@${occStart.getTime()}`,
        start: occStart,
        end: occEnd,
      })
    }
  }

  return out.sort((a, b) => a.start.getTime() - b.start.getTime())
}

/** Événements (déjà développés) qui couvrent un jour donné. */
export function eventsOnDay(events: AgendaEvent[], day: Date): AgendaEvent[] {
  const dayStart = new Date(day)
  dayStart.setHours(0, 0, 0, 0)
  const dayEnd = addDays(dayStart, 1)
  return events.filter((e) => e.start < dayEnd && e.end > dayStart)
}

export function isMultiDay(event: AgendaEvent): boolean {
  if (event.allDay) {
    return event.end.getTime() - event.start.getTime() > 24 * 3600 * 1000
  }
  const sameDay =
    event.start.getFullYear() === event.end.getFullYear() &&
    event.start.getMonth() === event.end.getMonth() &&
    event.start.getDate() === event.end.getDate()
  return !sameDay
}
