import { updateAssignment, updateDemandRequirement } from "@capakraken/application";
import type { PrismaClient } from "@capakraken/db";
import { calculateAllocation, validateShift } from "@capakraken/engine";
import { PermissionKey, ShiftProjectSchema } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { emitProjectShifted } from "../sse/event-bus.js";
import { createTRPCRouter, managerProcedure, requirePermission } from "../trpc.js";
import { buildAbsenceDays, loadCalculationRules, timelineAllocationMutationProcedures } from "./timeline-allocation-mutations.js";
import { timelineReadProcedures } from "./timeline-read.js";
import { loadProjectShiftContext } from "./timeline-project-read.js";

export const timelineRouter = createTRPCRouter({
  ...timelineReadProcedures,
  ...timelineAllocationMutationProcedures,

  /**
   * Apply a project shift: validate, then commit all allocation date changes.
   * Reads includeSaturday from each allocation's metadata.
   */
  applyShift: managerProcedure
    .input(ShiftProjectSchema)
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
      const { projectId, newStartDate, newEndDate } = input;
      const { project, demandRequirements, assignments, shiftPlan } = await loadProjectShiftContext(
        ctx.db,
        projectId,
      );

      const validation = validateShift({
        project: {
          id: project.id,
          budgetCents: project.budgetCents,
          winProbability: project.winProbability,
          startDate: project.startDate,
          endDate: project.endDate,
        },
        newStartDate,
        newEndDate,
        allocations: shiftPlan.validationAllocations,
      });

      if (!validation.valid) {
        throw new TRPCError({
          code: "BAD_REQUEST",
          message: `Shift validation failed: ${validation.errors.map((error) => error.message).join(", ")}`,
        });
      }

      const shiftRules = await loadCalculationRules(ctx.db as PrismaClient);

      const updatedProject = await ctx.db.$transaction(async (tx) => {
        const projectRecord = await tx.project.update({
          where: { id: projectId },
          data: { startDate: newStartDate, endDate: newEndDate },
        });

        for (const demandRequirement of demandRequirements) {
          await updateDemandRequirement(
            tx as unknown as Parameters<typeof updateDemandRequirement>[0],
            demandRequirement.id,
            {
              startDate: newStartDate,
              endDate: newEndDate,
            },
          );
        }

        for (const assignment of assignments) {
          const metadata = (assignment.metadata as Record<string, unknown> | null | undefined) ?? {};
          const includeSaturday = (metadata.includeSaturday as boolean | undefined) ?? false;

          const shiftAbsenceData = await buildAbsenceDays(
            ctx.db as PrismaClient,
            assignment.resourceId!,
            newStartDate,
            newEndDate,
          );

          const newDailyCost = calculateAllocation({
            lcrCents: assignment.resource!.lcrCents,
            hoursPerDay: assignment.hoursPerDay,
            startDate: newStartDate,
            endDate: newEndDate,
            availability:
              assignment.resource!.availability as unknown as import("@capakraken/shared").WeekdayAvailability,
            includeSaturday,
            vacationDates: shiftAbsenceData.legacyVacationDates,
            absenceDays: shiftAbsenceData.absenceDays,
            calculationRules: shiftRules,
          }).dailyCostCents;

          await updateAssignment(
            tx as unknown as Parameters<typeof updateAssignment>[0],
            assignment.id,
            {
              startDate: newStartDate,
              endDate: newEndDate,
              dailyCostCents: newDailyCost,
            },
          );
        }

        await tx.auditLog.create({
          data: {
            entityType: "Project",
            entityId: projectId,
            action: "SHIFT",
            changes: {
              before: { startDate: project.startDate, endDate: project.endDate },
              after: { startDate: newStartDate, endDate: newEndDate },
              costImpact: validation.costImpact,
            } as unknown as import("@capakraken/db").Prisma.InputJsonValue,
          },
        });

        return projectRecord;
      });

      emitProjectShifted({
        projectId,
        newStartDate: newStartDate.toISOString(),
        newEndDate: newEndDate.toISOString(),
        costDeltaCents: validation.costImpact.deltaCents,
        resourceIds: assignments.map((assignment) => assignment.resourceId),
      });

      return { project: updatedProject, validation };
    }),
});
