import { DateTime, Interval } from 'luxon';

export interface WeeklyRule {
  dayOfWeek: number;
  startTime: string;
  endTime: string;
  /** Optional location from host Locations module. */
  locationId?: string | null;
}

export interface TimeInterval {
  startTime: string;
  endTime: string;
}

export function parseTimeOnDate(date: string, time: string, timezone: string): DateTime {
  const [hour, minute] = time.split(':').map(Number);
  return DateTime.fromISO(date, { zone: timezone }).set({
    hour,
    minute,
    second: 0,
    millisecond: 0,
  });
}

export function intervalsOverlap(
  aStart: Date,
  aEnd: Date,
  bStart: Date,
  bEnd: Date,
): boolean {
  return aStart < bEnd && aEnd > bStart;
}

export function addMinutes(date: Date, minutes: number): Date {
  return DateTime.fromJSDate(date, { zone: 'utc' }).plus({ minutes }).toJSDate();
}

export function toIsoUtc(date: Date): string {
  return DateTime.fromJSDate(date, { zone: 'utc' }).toISO()!;
}

export function formatDisplayIso(date: Date, timezone: string): string {
  return DateTime.fromJSDate(date, { zone: 'utc' }).setZone(timezone).toISO()!;
}

export function eachDateInRange(from: string, to: string): string[] {
  const dates: string[] = [];
  let current = DateTime.fromISO(from);
  const end = DateTime.fromISO(to);
  while (current <= end) {
    dates.push(current.toISODate()!);
    current = current.plus({ days: 1 });
  }
  return dates;
}

export function getDayOfWeek(date: string, timezone: string): number {
  return DateTime.fromISO(date, { zone: timezone }).weekday % 7;
}

export function mergeIntervals(
  intervals: Array<{ start: Date; end: Date }>,
): Array<{ start: Date; end: Date }> {
  if (intervals.length === 0) return [];
  const sorted = [...intervals].sort((a, b) => a.start.getTime() - b.start.getTime());
  const merged: Array<{ start: Date; end: Date }> = [sorted[0]];
  for (let i = 1; i < sorted.length; i++) {
    const last = merged[merged.length - 1];
    const cur = sorted[i];
    if (cur.start <= last.end) {
      last.end = cur.end > last.end ? cur.end : last.end;
    } else {
      merged.push({ ...cur });
    }
  }
  return merged;
}

export function subtractBusyFromInterval(
  intervalStart: DateTime,
  intervalEnd: DateTime,
  busyBlocks: Array<{ start: Date; end: Date }>,
): Array<{ start: DateTime; end: DateTime }> {
  let free: Array<{ start: DateTime; end: DateTime }> = [
    { start: intervalStart, end: intervalEnd },
  ];

  for (const busy of busyBlocks) {
    const bStart = DateTime.fromJSDate(busy.start, { zone: 'utc' });
    const bEnd = DateTime.fromJSDate(busy.end, { zone: 'utc' });
    const nextFree: Array<{ start: DateTime; end: DateTime }> = [];

    for (const slot of free) {
      if (bEnd <= slot.start || bStart >= slot.end) {
        nextFree.push(slot);
        continue;
      }
      if (bStart > slot.start) {
        nextFree.push({ start: slot.start, end: bStart });
      }
      if (bEnd < slot.end) {
        nextFree.push({ start: bEnd, end: slot.end });
      }
    }
    free = nextFree;
  }

  return free.filter((f) => f.end > f.start);
}

export function isValidTimezone(tz: string): boolean {
  try {
    return DateTime.now().setZone(tz).isValid;
  } catch {
    return false;
  }
}

export { DateTime, Interval };
