import { validateAvailability } from "@capakraken/engine";
import {
  type AllocationConflictCheckResult,
  type WeekdayAvailability,
} from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { managerProcedure } from "../trpc.js";
import { toIsoDate } from "./allocation/shared.js";

const CheckConflictsInputSchema = z.object({
  resourceId: z.string(),
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
  hoursPerDay: z.number().min(0).max(24),
  /** Pass the current assignment id when editing so it is excluded from existing load. */
  excludeAssignmentId: z.string().optional(),
});

export const allocationConflictProcedures = {
  /**
   * Pre-flight conflict check for the allocation form.
   * Returns overbooking details (per-day breakdown) and vacation overlaps.
   * Read-only — no mutations.
   */
  checkConflicts: managerProcedure
    .input(CheckConflictsInputSchema)
    .query(async ({ ctx, input }): Promise<AllocationConflictCheckResult> => {
      const resource = await ctx.db.resource.findUnique({
        where: { id: input.resourceId },
        select: {
          availability: true,
          fte: true,
          country: { select: { dailyWorkingHours: true } },
        },
      });

      if (!resource) {
        throw new TRPCError({ code: "NOT_FOUND", message: "Resource not found" });
      }

      const fallbackDailyHours =
        (resource.country?.dailyWorkingHours ?? 8) * (resource.fte ?? 1);
      const availability = (resource.availability as WeekdayAvailability | null) ?? {
        monday: fallbackDailyHours,
        tuesday: fallbackDailyHours,
        wednesday: fallbackDailyHours,
        thursday: fallbackDailyHours,
        friday: fallbackDailyHours,
        saturday: 0,
        sunday: 0,
      };

      // Load existing active assignments for the resource in the period
      const existingAssignments = await ctx.db.assignment.findMany({
        where: {
          resourceId: input.resourceId,
          status: { not: "CANCELLED" },
          startDate: { lte: input.endDate },
          endDate: { gte: input.startDate },
          ...(input.excludeAssignmentId ? { id: { not: input.excludeAssignmentId } } : {}),
        },
        select: { startDate: true, endDate: true, hoursPerDay: true, status: true },
      });

      // Load approved vacations that overlap the requested period
      const vacations = await ctx.db.vacation.findMany({
        where: {
          resourceId: input.resourceId,
          status: "APPROVED",
          startDate: { lte: input.endDate },
          endDate: { gte: input.startDate },
        },
        select: { startDate: true, endDate: true, type: true, isHalfDay: true },
        orderBy: { startDate: "asc" },
      });

      // Run pure availability check (no DB access).
      // Cast status to the shared enum — Prisma and shared enums are string-compatible.
      const availabilityResult = validateAvailability(
        input.startDate,
        input.endDate,
        input.hoursPerDay,
        availability,
        existingAssignments as { startDate: Date; endDate: Date; hoursPerDay: number; status: import("@capakraken/shared").AllocationStatus }[],
      );

      // Compute max overbook percentage for the worst day
      let maxOverbookPercent = 0;
      for (const conflict of availabilityResult.conflicts) {
        const totalBooked = conflict.existingHours + conflict.requestedHours;
        const overbookPct =
          conflict.availableHours > 0
            ? ((totalBooked / conflict.availableHours) - 1) * 100
            : 100;
        if (overbookPct > maxOverbookPercent) maxOverbookPercent = overbookPct;
      }

      const isOverbooking = availabilityResult.totalConflictDays > 0;

      return {
        isOverbooking,
        overbooking: isOverbooking
          ? {
              conflictDays: availabilityResult.conflicts.map((c) => ({
                date: toIsoDate(c.date),
                availableHours: c.availableHours,
                existingHours: c.existingHours,
                requestedHours: c.requestedHours,
                overageHours: c.overageHours,
              })),
              totalConflictDays: availabilityResult.totalConflictDays,
              maxOverbookPercent: Math.round(maxOverbookPercent),
            }
          : null,
        vacationOverlap: vacations.map((v) => ({
          startDate: toIsoDate(v.startDate),
          endDate: toIsoDate(v.endDate),
          type: v.type,
          isHalfDay: v.isHalfDay,
        })),
        hasVacationOverlap: vacations.length > 0,
      };
    }),
};
