import { Prisma, VacationType } from "@capakraken/db";
import type { TRPCContext } from "../trpc.js";
import { loadResourceHolidayContext } from "./resource-holiday-context.js";
import { countVacationChargeableDays } from "./vacation-day-count.js";

export const VACATION_BALANCE_TYPES = new Set<VacationType>([
  VacationType.ANNUAL,
  VacationType.OTHER,
]);

export type VacationChargeableInput = {
  resourceId: string;
  type: VacationType;
  startDate: Date;
  endDate: Date;
  isHalfDay: boolean;
};

export type VacationDeductionSnapshot = {
  deductedDays: number;
  holidayCountryCode: string | null;
  holidayCountryName: string | null;
  holidayFederalState: string | null;
  holidayMetroCityName: string | null;
  holidayCalendarDates: string[];
  holidayLegacyPublicHolidayDates: string[];
};

type VacationSnapshotCarrier = {
  deductedDays?: number | null;
  holidayCountryCode?: string | null;
  holidayFederalState?: string | null;
  holidayMetroCityName?: string | null;
  holidayCalendarDates?: Prisma.JsonValue | null;
  holidayLegacyPublicHolidayDates?: Prisma.JsonValue | null;
  startDate: Date;
  endDate: Date;
  isHalfDay: boolean;
};

function isStringArray(value: unknown): value is string[] {
  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
}

export function parseVacationSnapshotDateList(value: Prisma.JsonValue | null | undefined): string[] {
  return isStringArray(value) ? [...value].sort() : [];
}

export async function calculateVacationDeductionSnapshot(
  db: TRPCContext["db"],
  vacation: VacationChargeableInput,
): Promise<VacationDeductionSnapshot> {
  const holidayContext = await loadResourceHolidayContext(
    db,
    vacation.resourceId,
    vacation.startDate,
    vacation.endDate,
  );

  return {
    deductedDays: countVacationChargeableDays({
      vacation,
      countryCode: holidayContext.countryCode,
      federalState: holidayContext.federalState,
      metroCityName: holidayContext.metroCityName,
      calendarHolidayStrings: holidayContext.calendarHolidayStrings,
      publicHolidayStrings: holidayContext.publicHolidayStrings,
    }),
    holidayCountryCode: holidayContext.countryCode ?? null,
    holidayCountryName: holidayContext.countryName ?? null,
    holidayFederalState: holidayContext.federalState ?? null,
    holidayMetroCityName: holidayContext.metroCityName ?? null,
    holidayCalendarDates: [...holidayContext.calendarHolidayStrings].sort(),
    holidayLegacyPublicHolidayDates: [...holidayContext.publicHolidayStrings].sort(),
  };
}

export function buildVacationDeductionSnapshotWriteData(
  snapshot: VacationDeductionSnapshot,
): {
  deductedDays: number;
  holidayCountryCode: string | null;
  holidayCountryName: string | null;
  holidayFederalState: string | null;
  holidayMetroCityName: string | null;
  holidayCalendarDates: Prisma.InputJsonValue;
  holidayLegacyPublicHolidayDates: Prisma.InputJsonValue;
} {
  return {
    deductedDays: snapshot.deductedDays,
    holidayCountryCode: snapshot.holidayCountryCode,
    holidayCountryName: snapshot.holidayCountryName,
    holidayFederalState: snapshot.holidayFederalState,
    holidayMetroCityName: snapshot.holidayMetroCityName,
    holidayCalendarDates: snapshot.holidayCalendarDates as unknown as Prisma.InputJsonValue,
    holidayLegacyPublicHolidayDates:
      snapshot.holidayLegacyPublicHolidayDates as unknown as Prisma.InputJsonValue,
  };
}

export function countVacationChargeableDaysFromSnapshot(
  vacation: VacationSnapshotCarrier,
  periodStart?: Date,
  periodEnd?: Date,
): number | null {
  if (vacation.deductedDays == null) {
    return null;
  }

  return countVacationChargeableDays({
    vacation,
    periodStart,
    periodEnd,
    countryCode: vacation.holidayCountryCode,
    federalState: vacation.holidayFederalState,
    metroCityName: vacation.holidayMetroCityName,
    calendarHolidayStrings: parseVacationSnapshotDateList(vacation.holidayCalendarDates),
    publicHolidayStrings: parseVacationSnapshotDateList(vacation.holidayLegacyPublicHolidayDates),
  });
}
