import type { PrismaClient, VacationStatus } from "@nexus/db";
import { TRPCError } from "@trpc/server";

type DbClient = Pick<PrismaClient, "vacation" | "resource">;

export type CancelVacationDeps = {
  assertVacationCancelable: (status: VacationStatus) => void;
  isVacationManagerRole: (role: string | null | undefined) => boolean;
  canActorCancelVacation: (input: {
    actorId: string;
    actorRole: string | null | undefined;
    requestedById: string | null | undefined;
    resourceUserId: string | null | undefined;
  }) => boolean;
};

export type CancelVacationInput = {
  id: string;
  actorId: string;
  actorRole: string | null | undefined;
};

export type CancelVacationResult = {
  vacation: Awaited<ReturnType<PrismaClient["vacation"]["update"]>>;
  existingStatus: VacationStatus;
};

export async function cancelVacation(
  db: DbClient,
  input: CancelVacationInput,
  deps: CancelVacationDeps,
): Promise<CancelVacationResult> {
  const existing = await db.vacation.findUnique({ where: { id: input.id } });
  if (!existing) {
    throw new TRPCError({ code: "NOT_FOUND", message: "Vacation not found" });
  }

  deps.assertVacationCancelable(existing.status);

  // Only fetch the linked resource when the actor is not a manager and didn't
  // originally request the vacation — we need to check resource ownership.
  const needsResourceCheck =
    !deps.isVacationManagerRole(input.actorRole) && existing.requestedById !== input.actorId;

  const resource = needsResourceCheck
    ? await db.resource.findUnique({
        where: { id: existing.resourceId },
        select: { userId: true },
      })
    : null;

  if (
    !deps.canActorCancelVacation({
      actorId: input.actorId,
      actorRole: input.actorRole,
      requestedById: existing.requestedById,
      resourceUserId: resource?.userId,
    })
  ) {
    throw new TRPCError({
      code: "FORBIDDEN",
      message: "You can only cancel your own vacation requests",
    });
  }

  const updated = await db.vacation.update({
    where: { id: input.id },
    data: { status: "CANCELLED" },
  });

  return { vacation: updated, existingStatus: existing.status };
}
