import type { ContactCard, CalendarEvent, Calendar, PartialDate, Timestamp } from '@/lib/jmap/types';
import { getContactDisplayName } from '@/stores/contact-store';
import { format, eachYearOfInterval, parseISO } from 'date-fns';

export const BIRTHDAY_CALENDAR_ID = '__birthday-calendar__';
export const BIRTHDAY_CALENDAR_COLOR = '#eab308'; // Yellow

/**
 * Virtual calendar object for the contact birthday calendar.
 */
export function createBirthdayCalendar(name?: string, color?: string): Calendar {
  return {
    id: BIRTHDAY_CALENDAR_ID,
    name: name || 'Birthdays',
    description: null,
    color: color || BIRTHDAY_CALENDAR_COLOR,
    sortOrder: 999,
    isSubscribed: true,
    isVisible: true,
    isDefault: false,
    includeInAvailability: 'none',
    defaultAlertsWithTime: null,
    defaultAlertsWithoutTime: null,
    timeZone: null,
    shareWith: null,
    myRights: {
      mayReadFreeBusy: true,
      mayReadItems: true,
      mayWriteAll: false,
      mayWriteOwn: false,
      mayUpdatePrivate: false,
      mayRSVP: false,
      mayShare: false,
      mayDelete: false,
    },
  };
}

/**
 * Extract month and day from an AnniversaryDate.
 * Returns null if the date cannot be parsed into month/day.
 */
function parseBirthdayDate(date: string | PartialDate | Timestamp): { month: number; day: number; year?: number } | null {
  if (typeof date === 'string') {
    // Could be ISO date string like "1990-05-15" or partial "--05-15"
    if (date.startsWith('--')) {
      // Partial date: --MM-DD
      const match = date.match(/^--(\d{2})-(\d{2})$/);
      if (match) {
        return { month: parseInt(match[1], 10), day: parseInt(match[2], 10) };
      }
      return null;
    }
    try {
      const parsed = parseISO(date);
      if (!isNaN(parsed.getTime())) {
        return {
          month: parsed.getMonth() + 1,
          day: parsed.getDate(),
          year: parsed.getFullYear(),
        };
      }
    } catch {
      return null;
    }
    return null;
  }

  if ('utc' in date && date['@type'] === 'Timestamp') {
    // Timestamp type
    try {
      const parsed = parseISO(date.utc);
      if (!isNaN(parsed.getTime())) {
        return {
          month: parsed.getMonth() + 1,
          day: parsed.getDate(),
          year: parsed.getFullYear(),
        };
      }
    } catch {
      return null;
    }
    return null;
  }

  // PartialDate type
  const partial = date as PartialDate;
  if (partial.month && partial.day) {
    return {
      month: partial.month,
      day: partial.day,
      year: partial.year || undefined,
    };
  }

  return null;
}

/**
 * Generate virtual CalendarEvent objects from contacts that have birthday anniversaries.
 * Events are generated for each year in the given date range.
 */
export function generateBirthdayEvents(
  contacts: ContactCard[],
  rangeStart: string,
  rangeEnd: string,
): CalendarEvent[] {
  const events: CalendarEvent[] = [];
  const start = parseISO(rangeStart);
  const end = parseISO(rangeEnd);

  if (isNaN(start.getTime()) || isNaN(end.getTime())) {
    return events;
  }

  const years = eachYearOfInterval({ start, end });
  // Also include the end date's year if not already covered
  const endYear = end.getFullYear();
  if (!years.some(y => y.getFullYear() === endYear)) {
    years.push(new Date(endYear, 0, 1));
  }

  for (const contact of contacts) {
    if (!contact.anniversaries) continue;

    for (const [key, anniversary] of Object.entries(contact.anniversaries)) {
      if (anniversary.kind !== 'birth') continue;

      const parsed = parseBirthdayDate(anniversary.date);
      if (!parsed) continue;

      const displayName = getContactDisplayName(contact);
      if (!displayName) continue;

      for (const yearDate of years) {
        const year = yearDate.getFullYear();
        // Handle Feb 29 in non-leap years: JS silently rolls over to Mar 1,
        // but the ISO eventStart string would be invalid. Clamp to Feb 28.
        let occMonth = parsed.month;
        let occDay = parsed.day;
        const occurrenceDate = new Date(year, occMonth - 1, occDay);
        if (occurrenceDate.getMonth() !== occMonth - 1) {
          occurrenceDate.setDate(0); // last day of previous month
          occMonth = occurrenceDate.getMonth() + 1;
          occDay = occurrenceDate.getDate();
        }
        if (occurrenceDate < start || occurrenceDate > end) continue;

        const monthStr = String(occMonth).padStart(2, '0');
        const dayStr = String(occDay).padStart(2, '0');
        const eventStart = `${year}-${monthStr}-${dayStr}T00:00:00`;

        const age = parsed.year ? year - parsed.year : undefined;
        const ageText = age && age > 0 ? ` (${age})` : '';

        const event: CalendarEvent = {
          id: `birthday-${contact.id}-${key}-${year}`,
          calendarIds: { [BIRTHDAY_CALENDAR_ID]: true },
          isDraft: false,
          isOrigin: false,
          utcStart: null,
          utcEnd: null,
          '@type': 'Event',
          uid: `birthday-${contact.id}-${key}`,
          title: `🎂 ${displayName}${ageText}`,
          description: '',
          descriptionContentType: 'text/plain',
          created: null,
          updated: '',
          sequence: 0,
          start: eventStart,
          duration: 'P1D',
          timeZone: null,
          showWithoutTime: true,
          status: 'confirmed',
          freeBusyStatus: 'free',
          privacy: 'public',
          color: null,
          keywords: null,
          categories: null,
          locale: null,
          replyTo: null,
          organizerCalendarAddress: null,
          participants: null,
          mayInviteSelf: false,
          mayInviteOthers: false,
          hideAttendees: false,
          recurrenceId: null,
          recurrenceIdTimeZone: null,
          recurrenceRules: null,
          recurrenceOverrides: null,
          excludedRecurrenceRules: null,
          useDefaultAlerts: false,
          alerts: null,
          locations: null,
          virtualLocations: null,
          links: null,
          relatedTo: null,
        };

        events.push(event);
      }
    }
  }

  return events;
}
