/**
 * hydrateEvents — convert raw API occurrences to Schedule-X event format.
 *
 * Schedule-X v4 requires:
 *   - Temporal.PlainDate  for all-day events
 *   - Temporal.ZonedDateTime for timed events
 * It does NOT accept ISO strings for start/end.
 *
 * CRITICAL — calendarId routing:
 *   occ.isShared  → 'shared'
 *   !occ.isShared → String(occ.ownerUserId)  ← NOT String(occ.calendarId)
 *
 * occ.calendarId is the DB calendar-row id. buildCalendarConfig() keys its
 * calendars config by String(userId) and 'shared'. A member owning multiple
 * calendars (e.g. "Calendar" + "USA Holidays") would produce a calendarId
 * matching no config key if we used String(calendarId), rendering those events
 * with no color. Routing by String(ownerUserId) keeps one color slot per member.
 *
 * Temporal is registered as a global by 'temporal-polyfill/global', imported
 * in main.tsx before any Schedule-X code mounts. Tests must import
 * 'temporal-polyfill/global' at the top of hydrateEvents.test.ts.
 *
 * Source: https://schedule-x.dev/docs/calendar/events
 */

export interface CalendarOccurrence {
  id: string; // `${uid}::${dtstart_iso}` — stable identity for Schedule-X
  uid: string;
  calendarId: number; // DB calendar-row id — NOT used for calendarId routing
  calendarName: string;
  ownerUserId: number; // DB user id — this IS the routing key for personal events
  color: string; // hex from users.color or shared-family constant
  isShared: boolean; // true when this event belongs to the shared-family calendar
  title: string;
  start: string; // 'YYYY-MM-DD' for all-day; ISO 8601 with IANA tz for timed
  end: string;
  allDay: boolean;
  location: string | null;
  description: string | null;
}

export interface ScheduleXEvent {
  id: string;
  title: string;
  start: Temporal.ZonedDateTime | Temporal.PlainDate;
  end: Temporal.ZonedDateTime | Temporal.PlainDate;
  /**
   * Schedule-X calendarId — keys into the calendars config built by
   * buildCalendarConfig(). Routing:
   *   'shared'           when isShared === true
   *   String(ownerUserId) when isShared === false
   */
  calendarId: string;
  location?: string;
  description?: string;
  /** FamilySync custom fields — carried through for popover rendering */
  _familySync: {
    uid: string;
    color: string;
    isShared: boolean;
  };
}

/**
 * Convert an array of CalendarOccurrence (server JSON) to Schedule-X events.
 *
 * All-day guard (Pitfall 2): allDay:true → Temporal.PlainDate.from(occ.start)
 * where occ.start is 'YYYY-MM-DD'. Never use ZonedDateTime for all-day events
 * or the date will shift in negative-offset timezones (UTC-N).
 */
export function hydrateEvents(occurrences: CalendarOccurrence[]): ScheduleXEvent[] {
  return occurrences.map((occ) => {
    // calendarId routing contract — must match buildCalendarConfig() keys
    const calendarId: string = occ.isShared ? 'shared' : String(occ.ownerUserId);

    if (occ.allDay) {
      // All-day: use Temporal.PlainDate — do NOT construct ZonedDateTime from
      // midnight UTC. occ.start and occ.end are 'YYYY-MM-DD' strings.
      //
      // Exclusive→inclusive end conversion: iCalendar all-day DTEND is EXCLUSIVE
      // (a single-day event on the 24th is DTSTART:24 / DTEND:25), and the server
      // occurrence carries that exclusive end. Schedule-X treats an all-day event's
      // `end` as INCLUSIVE (the last day it covers), so passing the exclusive DTEND
      // straight through renders every all-day event one day too long (a 1-day event
      // showed across two days). Subtract one day to get the inclusive last day,
      // clamped to never precede start.
      const startPd = Temporal.PlainDate.from(occ.start);
      const endExclusive = Temporal.PlainDate.from(occ.end);
      const endInclusive = endExclusive.subtract({ days: 1 });
      const end = Temporal.PlainDate.compare(endInclusive, startPd) < 0 ? startPd : endInclusive;
      return {
        id: occ.id,
        title: occ.title,
        start: startPd,
        end,
        calendarId,
        _familySync: {
          uid: occ.uid,
          color: occ.color,
          isShared: occ.isShared,
        },
      } satisfies ScheduleXEvent;
    }

    // Timed: use ZonedDateTime from the offset+IANA-annotated ISO string the server returns.
    // e.g. '2026-06-15T10:00:00-04:00[America/New_York]'
    return {
      id: occ.id,
      title: occ.title,
      start: Temporal.ZonedDateTime.from(occ.start),
      end: Temporal.ZonedDateTime.from(occ.end),
      calendarId,
      location: occ.location ?? undefined,
      description: occ.description ?? undefined,
      _familySync: {
        uid: occ.uid,
        color: occ.color,
        isShared: occ.isShared,
      },
    } satisfies ScheduleXEvent;
  });
}
