import {
  buildSplitAllocationReadModel,
  createAssignment,
} from "@capakraken/application";
import type { PrismaClient } from "@capakraken/db";
import {
  AllocationStatus,
  PermissionKey,
  UpdateAllocationHoursSchema,
} from "@capakraken/shared";
import { z } from "zod";
import {
  emitAllocationCreated,
  emitAllocationUpdated,
} from "../sse/event-bus.js";
import { managerProcedure, requirePermission } from "../trpc.js";
import {
  assertTimelineDateRangeValid,
  buildTimelineQuickAssignAssignmentInput,
  validateTimelineAllocationDateRanges,
} from "./timeline-allocation-mutation-support.js";
import { applyTimelineInlineAllocationUpdate } from "./timeline-allocation-inline-support.js";
import { applyTimelineBatchAllocationShift } from "./timeline-allocation-shift-support.js";

export const timelineAllocationMutationProcedures = {
  updateAllocationInline: managerProcedure
    .input(UpdateAllocationHoursSchema)
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
      const updated = await applyTimelineInlineAllocationUpdate({
        db: ctx.db as PrismaClient,
        allocationId: input.allocationId,
        hoursPerDay: input.hoursPerDay,
        startDate: input.startDate,
        endDate: input.endDate,
        includeSaturday: input.includeSaturday,
        role: input.role,
      });

      emitAllocationUpdated({
        id: updated.id,
        projectId: updated.projectId,
        resourceId: updated.resourceId,
      });

      return updated;
    }),

  quickAssign: managerProcedure
    .input(
      z.object({
        resourceId: z.string(),
        projectId: z.string(),
        startDate: z.coerce.date(),
        endDate: z.coerce.date(),
        hoursPerDay: z.number().min(0.5).max(24).default(8),
        role: z.string().min(1).max(200).default("Team Member"),
        roleId: z.string().optional(),
        status: z.nativeEnum(AllocationStatus).default(AllocationStatus.PROPOSED),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
      assertTimelineDateRangeValid(input.startDate, input.endDate);

      const allocation = await ctx.db.$transaction(async (tx) => {
        const assignment = await createAssignment(
          tx as unknown as Parameters<typeof createAssignment>[0],
          buildTimelineQuickAssignAssignmentInput({
            ...input,
            source: "quickAssign",
          }),
        );

        return buildSplitAllocationReadModel({
          demandRequirements: [],
          assignments: [assignment],
        }).allocations[0]!;
      });

      emitAllocationCreated({
        id: allocation.id,
        projectId: allocation.projectId,
        resourceId: allocation.resourceId,
      });

      return allocation;
    }),

  batchQuickAssign: managerProcedure
    .input(
      z.object({
        assignments: z
          .array(
            z.object({
              resourceId: z.string(),
              projectId: z.string(),
              startDate: z.coerce.date(),
              endDate: z.coerce.date(),
              hoursPerDay: z.number().min(0.5).max(24).default(8),
              role: z.string().min(1).max(200).default("Team Member"),
              status: z
                .nativeEnum(AllocationStatus)
                .default(AllocationStatus.PROPOSED),
            }),
          )
          .min(1)
          .max(50),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);
      validateTimelineAllocationDateRanges(input.assignments);

      const results = await ctx.db.$transaction(async (tx) => {
        const created = [];
        for (const assignment of input.assignments) {
          const createdAssignment = await createAssignment(
            tx as unknown as Parameters<typeof createAssignment>[0],
            buildTimelineQuickAssignAssignmentInput({
              ...assignment,
              source: "batchQuickAssign",
            }),
          );
          created.push(createdAssignment);
        }
        return created;
      });

      for (const assignment of results) {
        emitAllocationCreated({
          id: assignment.id,
          projectId: assignment.projectId,
          resourceId: assignment.resourceId,
        });
      }

      return { count: results.length };
    }),

  batchShiftAllocations: managerProcedure
    .input(
      z.object({
        allocationIds: z.array(z.string()).min(1).max(100),
        daysDelta: z.number().int().min(-3650).max(3650),
        mode: z.enum(["move", "resize-start", "resize-end"]).default("move"),
      }),
    )
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_ALLOCATIONS);

      const results = await applyTimelineBatchAllocationShift({
        db: ctx.db as PrismaClient,
        allocationIds: input.allocationIds,
        daysDelta: input.daysDelta,
        mode: input.mode,
      });

      for (const allocation of results) {
        emitAllocationUpdated({
          id: allocation.id,
          projectId: allocation.projectId,
          resourceId: allocation.resourceId,
        });
      }

      return { count: results.length };
    }),
};
