import { type WeekdayAvailability } from "@capakraken/shared";
import {
  calculateEffectiveAvailableHours,
  calculateEffectiveBookedHours,
  countEffectiveWorkingDays,
  type ResourceDailyAvailabilityContext,
} from "../lib/resource-capacity.js";
import { averagePerWorkingDay, getBaseDayAvailability, round1 } from "./staffing-shared.js";

type CapacityBooking = {
  startDate: Date;
  endDate: Date;
  hoursPerDay: number;
};

export function summarizeHolidayAndAbsenceCapacity(input: {
  availability: WeekdayAvailability;
  context: ResourceDailyAvailabilityContext | undefined;
}) {
  const holidayDates = [...(input.context?.holidayDates ?? new Set<string>())].sort();
  const holidayWorkdayCount = holidayDates.reduce((count, isoDate) => (
    count + (getBaseDayAvailability(input.availability, new Date(`${isoDate}T00:00:00.000Z`)) > 0 ? 1 : 0)
  ), 0);
  const holidayHoursDeduction = holidayDates.reduce((sum, isoDate) => (
    sum + getBaseDayAvailability(input.availability, new Date(`${isoDate}T00:00:00.000Z`))
  ), 0);

  let excludedCapacityDays = 0;
  for (const fraction of input.context?.absenceFractionsByDate.values() ?? []) {
    excludedCapacityDays += fraction;
  }

  let absenceDayEquivalent = 0;
  let absenceHoursDeduction = 0;
  for (const [isoDate, fraction] of input.context?.vacationFractionsByDate ?? []) {
    const dayHours = getBaseDayAvailability(input.availability, new Date(`${isoDate}T00:00:00.000Z`));
    if (dayHours <= 0 || input.context?.holidayDates.has(isoDate)) {
      continue;
    }
    absenceDayEquivalent += fraction;
    absenceHoursDeduction += dayHours * fraction;
  }

  return {
    excludedCapacityDays: round1(excludedCapacityDays),
    holidayDates,
    holidayWorkdayCount,
    holidayHoursDeduction: round1(holidayHoursDeduction),
    absenceDayEquivalent: round1(absenceDayEquivalent),
    absenceHoursDeduction: round1(absenceHoursDeduction),
  };
}

export function buildResourceCapacitySummary(input: {
  availability: WeekdayAvailability;
  periodStart: Date;
  periodEnd: Date;
  context: ResourceDailyAvailabilityContext | undefined;
  bookings?: CapacityBooking[] | undefined;
}) {
  const baseWorkingDays = countEffectiveWorkingDays({
    availability: input.availability,
    periodStart: input.periodStart,
    periodEnd: input.periodEnd,
    context: undefined,
  });
  const workingDays = countEffectiveWorkingDays({
    availability: input.availability,
    periodStart: input.periodStart,
    periodEnd: input.periodEnd,
    context: input.context,
  });
  const baseAvailableHours = calculateEffectiveAvailableHours({
    availability: input.availability,
    periodStart: input.periodStart,
    periodEnd: input.periodEnd,
    context: undefined,
  });
  const availableHours = calculateEffectiveAvailableHours({
    availability: input.availability,
    periodStart: input.periodStart,
    periodEnd: input.periodEnd,
    context: input.context,
  });
  const bookedHours = (input.bookings ?? []).reduce(
    (sum, booking) =>
      sum + calculateEffectiveBookedHours({
        availability: input.availability,
        startDate: booking.startDate,
        endDate: booking.endDate,
        hoursPerDay: booking.hoursPerDay,
        periodStart: input.periodStart,
        periodEnd: input.periodEnd,
        context: input.context,
      }),
    0,
  );
  const remainingHours = Math.max(0, availableHours - bookedHours);
  const holidayAndAbsence = summarizeHolidayAndAbsenceCapacity({
    availability: input.availability,
    context: input.context,
  });

  return {
    baseWorkingDays: round1(baseWorkingDays),
    workingDays: round1(workingDays),
    baseAvailableHours: round1(baseAvailableHours),
    availableHours: round1(availableHours),
    bookedHours: round1(bookedHours),
    remainingHours: round1(remainingHours),
    remainingHoursPerDay: averagePerWorkingDay(remainingHours, workingDays),
    holidaySummary: {
      count: holidayAndAbsence.holidayDates.length,
      workdayCount: holidayAndAbsence.holidayWorkdayCount,
      hoursDeduction: holidayAndAbsence.holidayHoursDeduction,
      holidayDates: holidayAndAbsence.holidayDates,
    },
    absenceSummary: {
      dayEquivalent: holidayAndAbsence.absenceDayEquivalent,
      hoursDeduction: holidayAndAbsence.absenceHoursDeduction,
    },
    excludedCapacityDays: holidayAndAbsence.excludedCapacityDays,
    capacityBreakdown: {
      formula: "baseAvailableHours - holidayHoursDeduction - absenceHoursDeduction = availableHours",
      baseAvailableHours: round1(baseAvailableHours),
      holidayHoursDeduction: holidayAndAbsence.holidayHoursDeduction,
      absenceHoursDeduction: holidayAndAbsence.absenceHoursDeduction,
      availableHours: round1(availableHours),
    },
  };
}
