import { Prisma } from "@capakraken/db";
import { AllocationStatus } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";

export type TimelineBatchShiftMode = "move" | "resize-start" | "resize-end";

export function assertTimelineDateRangeValid(startDate: Date, endDate: Date): void {
  if (endDate >= startDate) {
    return;
  }

  throw new TRPCError({
    code: "BAD_REQUEST",
    message: "End date must be after start date",
  });
}

export function buildTimelineAllocationMetadata(input: {
  existingMetadata: Record<string, unknown> | null | undefined;
  includeSaturday: boolean | undefined;
}): { metadata: Record<string, unknown>; includeSaturday: boolean } {
  const existingMetadata = input.existingMetadata ?? {};
  const metadata: Record<string, unknown> = {
    ...existingMetadata,
    ...(input.includeSaturday !== undefined
      ? { includeSaturday: input.includeSaturday }
      : {}),
  };
  const includeSaturday =
    input.includeSaturday ?? (existingMetadata.includeSaturday as boolean | undefined) ?? false;

  return { metadata, includeSaturday };
}

export function calculateTimelineAllocationPercentage(hoursPerDay: number): number {
  return Math.min(100, Math.round((hoursPerDay / 8) * 100));
}

export function buildTimelineQuickAssignMetadata(source: "quickAssign" | "batchQuickAssign") {
  return { source } satisfies Record<string, unknown>;
}

export function buildTimelineQuickAssignAssignmentInput(input: {
  resourceId: string;
  projectId: string;
  startDate: Date;
  endDate: Date;
  hoursPerDay: number;
  role: string;
  roleId?: string | undefined;
  status: AllocationStatus;
  source: "quickAssign" | "batchQuickAssign";
}) {
  return {
    resourceId: input.resourceId,
    projectId: input.projectId,
    startDate: input.startDate,
    endDate: input.endDate,
    hoursPerDay: input.hoursPerDay,
    percentage: calculateTimelineAllocationPercentage(input.hoursPerDay),
    role: input.role,
    ...(input.roleId !== undefined ? { roleId: input.roleId } : {}),
    status: input.status,
    metadata: buildTimelineQuickAssignMetadata(input.source),
  };
}

export function validateTimelineAllocationDateRanges(
  ranges: Array<{ startDate: Date; endDate: Date }>,
): void {
  for (const range of ranges) {
    assertTimelineDateRangeValid(range.startDate, range.endDate);
  }
}

export function shiftTimelineAllocationWindow(input: {
  startDate: Date;
  endDate: Date;
  daysDelta: number;
  mode: TimelineBatchShiftMode;
}): { startDate: Date; endDate: Date } {
  const startDate = new Date(input.startDate);
  const endDate = new Date(input.endDate);

  if (input.mode === "move") {
    startDate.setDate(startDate.getDate() + input.daysDelta);
    endDate.setDate(endDate.getDate() + input.daysDelta);
    return { startDate, endDate };
  }

  if (input.mode === "resize-start") {
    startDate.setDate(startDate.getDate() + input.daysDelta);
    if (startDate > endDate) {
      startDate.setTime(endDate.getTime());
    }
    return { startDate, endDate };
  }

  endDate.setDate(endDate.getDate() + input.daysDelta);
  if (endDate < startDate) {
    endDate.setTime(startDate.getTime());
  }

  return { startDate, endDate };
}

export function buildTimelineAllocationUpdateAuditChanges(input: {
  allocationId: string;
  previousHoursPerDay: number;
  previousStartDate: Date;
  previousEndDate: Date;
  nextAllocationId: string;
  nextHoursPerDay: number;
  nextStartDate: Date;
  nextEndDate: Date;
  includeSaturday: boolean;
}): Prisma.InputJsonValue {
  return {
    before: {
      id: input.allocationId,
      hoursPerDay: input.previousHoursPerDay,
      startDate: input.previousStartDate,
      endDate: input.previousEndDate,
    },
    after: {
      id: input.nextAllocationId,
      hoursPerDay: input.nextHoursPerDay,
      startDate: input.nextStartDate,
      endDate: input.nextEndDate,
      includeSaturday: input.includeSaturday,
    },
  } as unknown as Prisma.InputJsonValue;
}

export function buildTimelineAllocationEntryUpdate(input: {
  hoursPerDay: number;
  startDate: Date;
  endDate: Date;
  metadata: Record<string, unknown>;
  dailyCostCents: number;
  role?: string | undefined;
}) {
  return {
    demandRequirementUpdate: {
      hoursPerDay: input.hoursPerDay,
      startDate: input.startDate,
      endDate: input.endDate,
      metadata: input.metadata,
      ...(input.role !== undefined ? { role: input.role } : {}),
    },
    assignmentUpdate: {
      hoursPerDay: input.hoursPerDay,
      startDate: input.startDate,
      endDate: input.endDate,
      dailyCostCents: input.dailyCostCents,
      metadata: input.metadata,
      ...(input.role !== undefined ? { role: input.role } : {}),
    },
  };
}

export function buildTimelineBatchShiftAuditChanges(input: {
  mode: TimelineBatchShiftMode;
  daysDelta: number;
  count: number;
}): Prisma.InputJsonValue {
  return {
    operation: "batchShift",
    mode: input.mode,
    daysDelta: input.daysDelta,
    count: input.count,
  } as unknown as Prisma.InputJsonValue;
}
