import { VacationStatus, VacationType } from "@capakraken/db";
import { getResolvedCalendarHolidayStrings, toIsoDate } from "./holiday-availability.js";

type ResourceHolidayContextDb = {
  resource: {
    findUnique: (args: any) => any;
  };
  country?: {
    findUnique: (args: any) => any;
  };
  metroCity?: {
    findUnique: (args: any) => any;
  };
  holidayCalendar?: {
    findMany: (args: any) => any;
  };
  vacation: {
    findMany: (args: any) => any;
  };
};

export type ResourceHolidayContext = {
  countryId?: string | null;
  countryCode?: string | null;
  countryName?: string | null;
  federalState?: string | null;
  metroCityId?: string | null;
  metroCityName?: string | null;
  calendarHolidayStrings: string[];
  publicHolidayStrings: string[];
};

function clampToDay(value: Date): Date {
  const date = new Date(value);
  date.setUTCHours(0, 0, 0, 0);
  return date;
}

export async function loadResourceHolidayContext(
  db: ResourceHolidayContextDb,
  resourceId: string,
  periodStart: Date,
  periodEnd: Date,
): Promise<ResourceHolidayContext> {
  const resource = typeof db.resource?.findUnique === "function"
    ? await db.resource.findUnique({
        where: { id: resourceId },
        select: {
          federalState: true,
          countryId: true,
          metroCityId: true,
          country: { select: { code: true, name: true } },
          metroCity: { select: { name: true } },
        },
      })
    : null;

  const holidayVacations = typeof db.vacation?.findMany === "function"
    ? await db.vacation.findMany({
        where: {
          resourceId,
          type: VacationType.PUBLIC_HOLIDAY,
          status: VacationStatus.APPROVED,
          startDate: { lte: periodEnd },
          endDate: { gte: periodStart },
        },
        select: { startDate: true, endDate: true },
      })
    : [];

  const calendarHolidayStrings = await getResolvedCalendarHolidayStrings(db, {
    periodStart,
    periodEnd,
    countryId: resource?.countryId ?? null,
    countryCode: resource?.country?.code ?? null,
    federalState: resource?.federalState ?? null,
    metroCityId: resource?.metroCityId ?? null,
    metroCityName: resource?.metroCity?.name ?? null,
  });
  const publicHolidayStrings = new Set<string>();

  for (const holiday of holidayVacations) {
    const cursor = clampToDay(new Date(Math.max(holiday.startDate.getTime(), periodStart.getTime())));
    const end = clampToDay(new Date(Math.min(holiday.endDate.getTime(), periodEnd.getTime())));

    while (cursor <= end) {
      publicHolidayStrings.add(toIsoDate(cursor));
      cursor.setUTCDate(cursor.getUTCDate() + 1);
    }
  }

  return {
    countryId: resource?.countryId ?? null,
    countryCode: resource?.country?.code ?? null,
    countryName: resource?.country?.name ?? null,
    federalState: resource?.federalState ?? null,
    metroCityId: resource?.metroCityId ?? null,
    metroCityName: resource?.metroCity?.name ?? null,
    calendarHolidayStrings,
    publicHolidayStrings: [...publicHolidayStrings].sort(),
  };
}
