import type { Prisma, PrismaClient } from "@capakraken/db";

type AssignmentBookingsDbClient = Pick<PrismaClient, "assignment">;

export interface ListAssignmentBookingsInput {
  startDate?: Date | undefined;
  endDate?: Date | undefined;
  resourceIds?: string[] | undefined;
  projectIds?: string[] | undefined;
  excludeAssignmentIds?: string[] | undefined;
}

export interface AssignmentBookingWithFallback {
  id: string;
  projectId: string;
  resourceId: string | null;
  startDate: Date;
  endDate: Date;
  hoursPerDay: number;
  dailyCostCents: number;
  status: string;
  project: {
    id: string;
    name: string;
    shortCode: string;
    status: string;
    orderType: string;
    clientId: string | null;
    dynamicFields: Prisma.JsonValue | null;
  };
  resource: {
    id: string;
    displayName: string;
    chapter: string | null;
  } | null;
}

export async function listAssignmentBookings(
  db: AssignmentBookingsDbClient,
  input: ListAssignmentBookingsInput,
): Promise<AssignmentBookingWithFallback[]> {
  const hasDateBounds = input.startDate !== undefined && input.endDate !== undefined;
  if (!hasDateBounds && (input.startDate !== undefined || input.endDate !== undefined)) {
    throw new Error("startDate and endDate must be provided together");
  }

  const excludeAssignmentIds = input.excludeAssignmentIds?.filter(Boolean) ?? [];

  const assignmentWhere = {
    status: { not: "CANCELLED" as const },
    ...(hasDateBounds
      ? {
          startDate: { lte: input.endDate! },
          endDate: { gte: input.startDate! },
        }
      : {}),
    ...(input.resourceIds?.length ? { resourceId: { in: input.resourceIds } } : {}),
    ...(input.projectIds?.length ? { projectId: { in: input.projectIds } } : {}),
    ...(excludeAssignmentIds.length ? { id: { notIn: excludeAssignmentIds } } : {}),
  } satisfies Prisma.AssignmentWhereInput;

  const assignmentSelect = {
    id: true,
    projectId: true,
    resourceId: true,
    startDate: true,
    endDate: true,
    hoursPerDay: true,
    dailyCostCents: true,
    status: true,
    project: {
      select: {
        id: true,
        name: true,
        shortCode: true,
        status: true,
        orderType: true,
        clientId: true,
        dynamicFields: true,
      },
    },
    resource: {
      select: { id: true, displayName: true, chapter: true },
    },
  } satisfies Prisma.AssignmentSelect;

  const assignments = await db.assignment.findMany({
    where: assignmentWhere,
    select: assignmentSelect,
  });

  return assignments.map((assignment) => ({
    id: assignment.id,
    projectId: assignment.projectId,
    resourceId: assignment.resourceId,
    startDate: assignment.startDate,
    endDate: assignment.endDate,
    hoursPerDay: assignment.hoursPerDay,
    dailyCostCents: assignment.dailyCostCents,
    status: assignment.status,
    project: assignment.project,
    resource: assignment.resource,
  }));
}
