import type { RecurrencePattern } from "@capakraken/shared";
import { RecurrenceFrequency } from "@capakraken/shared";

/**
 * Returns the ISO week number of a date relative to a base date.
 * Used for biweekly parity checks.
 */
function weeksSince(base: Date, date: Date): number {
  const msPerWeek = 7 * 24 * 60 * 60 * 1000;
  const baseMonday = new Date(base);
  baseMonday.setHours(0, 0, 0, 0);
  // Normalize to start of the week containing base
  const baseDow = baseMonday.getDay();
  baseMonday.setDate(baseMonday.getDate() - baseDow);

  const targetMonday = new Date(date);
  targetMonday.setHours(0, 0, 0, 0);
  const targetDow = targetMonday.getDay();
  targetMonday.setDate(targetMonday.getDate() - targetDow);

  return Math.round((targetMonday.getTime() - baseMonday.getTime()) / msPerWeek);
}

/**
 * Determines whether a given date falls on a "recurring day" according to the pattern.
 *
 * @param date               The calendar date to check
 * @param pattern            Recurrence pattern from allocation metadata
 * @param allocationStartDate The allocation's own start date (used for biweekly parity)
 */
export function isRecurringDay(
  date: Date,
  pattern: RecurrencePattern,
  allocationStartDate: Date,
): boolean {
  const dow = date.getDay(); // 0=Sun, 1=Mon … 6=Sat

  switch (pattern.frequency) {
    case RecurrenceFrequency.WEEKLY: {
      const weekdays = pattern.weekdays ?? [];
      return weekdays.includes(dow);
    }

    case RecurrenceFrequency.BIWEEKLY: {
      const weekdays = pattern.weekdays ?? [];
      if (!weekdays.includes(dow)) return false;
      // Check week parity: week 0 (same as allocationStart) = active,
      // odd weeks = skip, even weeks = active.
      const interval = pattern.interval ?? 2;
      const weeks = weeksSince(allocationStartDate, date);
      return weeks % interval === 0;
    }

    case RecurrenceFrequency.MONTHLY: {
      const monthDay = pattern.monthDay;
      if (monthDay == null) return false;
      return date.getDate() === monthDay;
    }

    case RecurrenceFrequency.CUSTOM:
      // CUSTOM means "always active, but with custom hoursPerDay"
      return true;

    default:
      return true;
  }
}

/**
 * Returns the effective hours for a day based on the recurrence pattern.
 * Returns 0 when the day is not a recurring day.
 *
 * @param date                 The calendar date
 * @param pattern              Recurrence pattern from allocation metadata
 * @param defaultHoursPerDay   The allocation's base hoursPerDay
 * @param allocationStartDate  The allocation's own start date
 */
export function getRecurringHoursForDay(
  date: Date,
  pattern: RecurrencePattern,
  defaultHoursPerDay: number,
  allocationStartDate: Date,
): number {
  // Respect optional start/end overrides on the pattern itself
  if (pattern.startDate) {
    const ps = new Date(pattern.startDate);
    ps.setHours(0, 0, 0, 0);
    if (date < ps) return 0;
  }
  if (pattern.endDate) {
    const pe = new Date(pattern.endDate);
    pe.setHours(0, 0, 0, 0);
    if (date > pe) return 0;
  }

  if (!isRecurringDay(date, pattern, allocationStartDate)) return 0;
  return pattern.hoursPerDay ?? defaultHoursPerDay;
}
