import {
  approveEstimateVersion,
  createEstimateRevision,
  submitEstimateVersion,
} from "@capakraken/application";
import type { Prisma } from "@capakraken/db";
import {
  ApproveEstimateVersionSchema,
  CreateEstimateRevisionSchema,
  PermissionKey,
  SubmitEstimateVersionSchema,
} from "@capakraken/shared";
import { managerProcedure, requirePermission } from "../trpc.js";
import { rethrowEstimateRouterError } from "./estimate-procedure-support.js";

export const estimateVersionWorkflowProcedures = {
  submitVersion: managerProcedure
    .input(SubmitEstimateVersionSchema)
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      let estimate;
      try {
        estimate = await ctx.db.$transaction(async (tx) => {
          const submitted = await submitEstimateVersion(
            tx as unknown as Parameters<typeof submitEstimateVersion>[0],
            input,
          );

          await tx.auditLog.create({
            data: {
              entityType: "Estimate",
              entityId: submitted.id,
              action: "UPDATE",
              ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
              changes: {
                after: {
                  id: submitted.id,
                  status: submitted.status,
                  submittedVersionId: submitted.versions.find(
                    (version) => version.status === "SUBMITTED",
                  )?.id,
                },
              } as Prisma.InputJsonValue,
            },
          });

          return submitted;
        });
      } catch (error) {
        rethrowEstimateRouterError(error, [
          {
            code: "NOT_FOUND",
            messages: ["Estimate not found", "Estimate version not found"],
          },
          {
            code: "PRECONDITION_FAILED",
            messages: [
              "Estimate has no working version",
              "Only working versions can be submitted",
            ],
          },
        ]);
      }

      return estimate;
    }),

  approveVersion: managerProcedure
    .input(ApproveEstimateVersionSchema)
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      let estimate;
      try {
        estimate = await ctx.db.$transaction(async (tx) => {
          const approved = await approveEstimateVersion(
            tx as unknown as Parameters<typeof approveEstimateVersion>[0],
            input,
          );

          await tx.auditLog.create({
            data: {
              entityType: "Estimate",
              entityId: approved.id,
              action: "UPDATE",
              ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
              changes: {
                after: {
                  id: approved.id,
                  status: approved.status,
                  approvedVersionId: approved.versions.find(
                    (version) => version.status === "APPROVED",
                  )?.id,
                },
              } as Prisma.InputJsonValue,
            },
          });

          return approved;
        });
      } catch (error) {
        rethrowEstimateRouterError(error, [
          {
            code: "NOT_FOUND",
            messages: ["Estimate not found", "Estimate version not found"],
          },
          {
            code: "PRECONDITION_FAILED",
            messages: [
              "Estimate has no submitted version",
              "Only submitted versions can be approved",
            ],
          },
        ]);
      }

      return estimate;
    }),

  createRevision: managerProcedure
    .input(CreateEstimateRevisionSchema)
    .mutation(async ({ ctx, input }) => {
      requirePermission(ctx, PermissionKey.MANAGE_PROJECTS);

      let estimate;
      try {
        estimate = await ctx.db.$transaction(async (tx) => {
          const revision = await createEstimateRevision(
            tx as unknown as Parameters<typeof createEstimateRevision>[0],
            input,
          );

          await tx.auditLog.create({
            data: {
              entityType: "Estimate",
              entityId: revision.id,
              action: "UPDATE",
              ...(ctx.dbUser?.id ? { userId: ctx.dbUser.id } : {}),
              changes: {
                after: {
                  id: revision.id,
                  status: revision.status,
                  latestVersionNumber: revision.latestVersionNumber,
                  workingVersionId: revision.versions.find(
                    (version) => version.status === "WORKING",
                  )?.id,
                },
              } as Prisma.InputJsonValue,
            },
          });

          return revision;
        });
      } catch (error) {
        rethrowEstimateRouterError(error, [
          {
            code: "NOT_FOUND",
            messages: ["Estimate not found", "Estimate version not found"],
          },
          {
            code: "PRECONDITION_FAILED",
            messages: [
              "Estimate already has a working version",
              "Estimate has no locked version to revise",
              "Source version must be locked before creating a revision",
            ],
          },
        ]);
      }

      return estimate;
    }),
};
