import type { Prisma } from "@capakraken/db";
import { VacationStatus } from "@capakraken/db";
import type { UpdateVacationStatusInput } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";

type VacationApprovalWriteData = Record<string, unknown>;

export const approvableVacationStatuses: readonly VacationStatus[] = [
  VacationStatus.PENDING,
  VacationStatus.CANCELLED,
  VacationStatus.REJECTED,
];

export function assertVacationApprovable(status: VacationStatus): void {
  if (!approvableVacationStatuses.includes(status)) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Only PENDING, CANCELLED, or REJECTED vacations can be approved",
    });
  }
}

export function assertVacationRejectable(status: VacationStatus): void {
  if (status !== VacationStatus.PENDING) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Only PENDING vacations can be rejected",
    });
  }
}

export function assertVacationCancelable(status: VacationStatus): void {
  if (status === VacationStatus.CANCELLED) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Already cancelled",
    });
  }
}

export function isVacationManagerRole(role: string | null | undefined): boolean {
  return role === "ADMIN" || role === "MANAGER";
}

export function canActorCancelVacation(input: {
  actorId: string;
  actorRole: string | null | undefined;
  requestedById: string | null | undefined;
  resourceUserId: string | null | undefined;
}): boolean {
  if (isVacationManagerRole(input.actorRole)) {
    return true;
  }

  return input.requestedById === input.actorId || input.resourceUserId === input.actorId;
}

export function buildApprovedVacationUpdateData(input: {
  deductionSnapshotWriteData: VacationApprovalWriteData;
  approvedById?: string | undefined;
  approvedAt: Date;
}): Prisma.VacationUncheckedUpdateInput {
  return {
    status: VacationStatus.APPROVED,
    rejectionReason: null,
    ...input.deductionSnapshotWriteData,
    ...(input.approvedById ? { approvedById: input.approvedById } : {}),
    approvedAt: input.approvedAt,
  };
}

export function buildRejectedVacationUpdateData(input: {
  rejectionReason?: string | undefined;
}): Prisma.VacationUncheckedUpdateInput {
  return {
    status: VacationStatus.REJECTED,
    ...(input.rejectionReason !== undefined ? { rejectionReason: input.rejectionReason } : {}),
  };
}

export function buildVacationStatusUpdateData(input: {
  status: UpdateVacationStatusInput["status"];
  note?: string | undefined;
  approvedById: string;
  approvedAt: Date;
}): Prisma.VacationUncheckedUpdateInput {
  const data: Prisma.VacationUncheckedUpdateInput = { status: input.status };

  if (input.status === VacationStatus.APPROVED) {
    data.approvedById = input.approvedById;
    data.approvedAt = input.approvedAt;
    data.rejectionReason = null;
  }
  if (input.note !== undefined) {
    data.note = input.note;
  }

  return data;
}
