import { TRPCError } from "@trpc/server";
import { countCalendarDaysInPeriod } from "../lib/vacation-day-count.js";
import {
  VACATION_BALANCE_TYPES,
  buildVacationDeductionSnapshotWriteData,
  calculateVacationDeductionSnapshot,
  type VacationChargeableInput,
} from "../lib/vacation-deduction-snapshot.js";
import type { TRPCContext } from "../trpc.js";

type VacationDb = TRPCContext["db"];
type VacationDeductionWriteData = ReturnType<typeof buildVacationDeductionSnapshotWriteData>;

export async function calculateVacationEffectiveDays(
  db: VacationDb,
  vacation: VacationChargeableInput,
): Promise<number> {
  if (!VACATION_BALANCE_TYPES.has(vacation.type)) {
    return countCalendarDaysInPeriod(vacation);
  }

  const snapshot = await calculateVacationDeductionSnapshot(db, vacation);
  return snapshot.deductedDays;
}

export async function assertVacationStillChargeable(
  db: VacationDb,
  vacation: VacationChargeableInput,
): Promise<void> {
  if (!VACATION_BALANCE_TYPES.has(vacation.type)) {
    return;
  }

  const effectiveDays = await calculateVacationEffectiveDays(db, vacation);
  if (effectiveDays <= 0) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Vacation no longer deducts any vacation days for the current holiday calendar and cannot be approved",
    });
  }
}

export async function resolveVacationCreationChargeability(
  db: VacationDb,
  vacation: VacationChargeableInput,
): Promise<{
  effectiveDays: number | null;
  deductionSnapshotWriteData: VacationDeductionWriteData | null;
}> {
  if (!VACATION_BALANCE_TYPES.has(vacation.type)) {
    return {
      effectiveDays: null,
      deductionSnapshotWriteData: null,
    };
  }

  const deductionSnapshot = await calculateVacationDeductionSnapshot(db, vacation);
  const effectiveDays = deductionSnapshot.deductedDays;

  if (effectiveDays <= 0) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Selected vacation period only contains public holidays and does not deduct any vacation days",
    });
  }

  return {
    effectiveDays,
    deductionSnapshotWriteData: buildVacationDeductionSnapshotWriteData(deductionSnapshot),
  };
}

export async function buildVacationApprovalWriteData(
  db: VacationDb,
  vacation: VacationChargeableInput,
): Promise<VacationDeductionWriteData | { deductedDays: 0 }> {
  if (!VACATION_BALANCE_TYPES.has(vacation.type)) {
    return { deductedDays: 0 };
  }

  return buildVacationDeductionSnapshotWriteData(
    await calculateVacationDeductionSnapshot(db, vacation),
  );
}
