import { BlueprintTarget, CreateProjectSchema, PermissionKey, UpdateProjectSchema } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { findUniqueOrThrow } from "../db/helpers.js";
import { assertBlueprintDynamicFields } from "./blueprint-validation.js";
import { managerProcedure, requirePermission, type TRPCContext } from "../trpc.js";
import type { ProjectBackgroundEffects } from "./project-background-effects.js";

function buildProjectCreateData(
  input: z.infer<typeof CreateProjectSchema>,
): Parameters<TRPCContext["db"]["project"]["create"]>[0]["data"] {
  return {
    shortCode: input.shortCode,
    name: input.name,
    orderType: input.orderType,
    allocationType: input.allocationType,
    winProbability: input.winProbability,
    budgetCents: input.budgetCents,
    startDate: input.startDate,
    endDate: input.endDate,
    status: input.status,
    responsiblePerson: input.responsiblePerson,
    ...(input.color !== undefined ? { color: input.color } : {}),
    staffingReqs: input.staffingReqs as unknown as import("@capakraken/db").Prisma.InputJsonValue,
    dynamicFields: input.dynamicFields as unknown as import("@capakraken/db").Prisma.InputJsonValue,
    blueprintId: input.blueprintId,
    ...(input.utilizationCategoryId !== undefined ? { utilizationCategoryId: input.utilizationCategoryId || null } : {}),
    ...(input.clientId !== undefined ? { clientId: input.clientId || null } : {}),
  } as unknown as Parameters<TRPCContext["db"]["project"]["create"]>[0]["data"];
}

function buildProjectUpdateData(
  input: z.infer<typeof UpdateProjectSchema>,
): Parameters<TRPCContext["db"]["project"]["update"]>[0]["data"] {
  return {
    ...(input.name !== undefined ? { name: input.name } : {}),
    ...(input.orderType !== undefined ? { orderType: input.orderType } : {}),
    ...(input.allocationType !== undefined ? { allocationType: input.allocationType } : {}),
    ...(input.winProbability !== undefined ? { winProbability: input.winProbability } : {}),
    ...(input.budgetCents !== undefined ? { budgetCents: input.budgetCents } : {}),
    ...(input.startDate !== undefined ? { startDate: input.startDate } : {}),
    ...(input.endDate !== undefined ? { endDate: input.endDate } : {}),
    ...(input.status !== undefined ? { status: input.status } : {}),
    ...(input.responsiblePerson !== undefined ? { responsiblePerson: input.responsiblePerson } : {}),
    ...(input.color !== undefined ? { color: input.color } : {}),
    ...(input.staffingReqs !== undefined ? { staffingReqs: input.staffingReqs as unknown as import("@capakraken/db").Prisma.InputJsonValue } : {}),
    ...(input.dynamicFields !== undefined ? { dynamicFields: input.dynamicFields as unknown as import("@capakraken/db").Prisma.InputJsonValue } : {}),
    ...(input.blueprintId !== undefined ? { blueprintId: input.blueprintId } : {}),
    ...(input.utilizationCategoryId !== undefined ? { utilizationCategoryId: input.utilizationCategoryId || null } : {}),
    ...(input.clientId !== undefined ? { clientId: input.clientId || null } : {}),
    ...(input.shoringThreshold !== undefined ? { shoringThreshold: input.shoringThreshold } : {}),
    ...(input.onshoreCountryCode !== undefined ? { onshoreCountryCode: input.onshoreCountryCode } : {}),
  } as unknown as Parameters<TRPCContext["db"]["project"]["update"]>[0]["data"];
}

export function createProjectMutationProcedures(
  backgroundEffects: ProjectBackgroundEffects,
) {
  return {
    create: managerProcedure
      .input(CreateProjectSchema)
      .mutation(async ({ ctx, input }) => {
        requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

        const existing = await ctx.db.project.findUnique({
          where: { shortCode: input.shortCode },
        });

        if (existing) {
          throw new TRPCError({
            code: "CONFLICT",
            message: `Project with short code "${input.shortCode}" already exists`,
          });
        }

        await assertBlueprintDynamicFields({
          db: ctx.db,
          blueprintId: input.blueprintId,
          dynamicFields: input.dynamicFields,
          target: BlueprintTarget.PROJECT,
        });

        const project = await ctx.db.project.create({
          data: buildProjectCreateData(input),
        });

        await ctx.db.auditLog.create({
          data: {
            entityType: "Project",
            entityId: project.id,
            action: "CREATE",
            changes: { after: project },
          },
        });

        backgroundEffects.invalidateDashboardCacheInBackground();
        backgroundEffects.dispatchProjectWebhookInBackground(ctx.db, "project.created", {
          id: project.id,
          shortCode: project.shortCode,
          name: project.name,
          status: project.status,
        });

        return project;
      }),

    update: managerProcedure
      .input(z.object({ id: z.string(), data: UpdateProjectSchema }))
      .mutation(async ({ ctx, input }) => {
        requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

        const existing = await findUniqueOrThrow(
          ctx.db.project.findUnique({ where: { id: input.id } }),
          "Project",
        );

        const nextBlueprintId = input.data.blueprintId ?? existing.blueprintId ?? undefined;
        const nextDynamicFields = (input.data.dynamicFields ?? existing.dynamicFields ?? {}) as Record<string, unknown>;

        await assertBlueprintDynamicFields({
          db: ctx.db,
          blueprintId: nextBlueprintId,
          dynamicFields: nextDynamicFields,
          target: BlueprintTarget.PROJECT,
        });

        const updated = await ctx.db.project.update({
          where: { id: input.id },
          data: buildProjectUpdateData(input.data),
        });

        await ctx.db.auditLog.create({
          data: {
            entityType: "Project",
            entityId: input.id,
            action: "UPDATE",
            changes: { before: existing, after: updated },
          },
        });

        backgroundEffects.invalidateDashboardCacheInBackground();
        return updated;
      }),
  };
}
