import { loadAllocationEntry, updateAllocationEntry } from "@capakraken/application";
import type { PrismaClient } from "@capakraken/db";
import type { RecurrencePattern, WeekdayAvailability } from "@capakraken/shared";
import { TRPCError } from "@trpc/server";
import { assertTimelineDateRangeValid } from "./timeline-allocation-mutation-support.js";
import {
  buildTimelineAllocationEntryUpdate,
  buildTimelineAllocationMetadata,
  buildTimelineAllocationUpdateAuditChanges,
} from "./timeline-allocation-update-support.js";
import { calculateTimelineAllocationDailyCost } from "./timeline-cost-support.js";

export async function applyTimelineInlineAllocationUpdate(input: {
  db: PrismaClient;
  allocationId: string;
  hoursPerDay?: number | undefined;
  startDate?: Date | undefined;
  endDate?: Date | undefined;
  includeSaturday?: boolean | undefined;
  role?: string | undefined;
}) {
  const resolved = await loadAllocationEntry(input.db, input.allocationId);
  const existing = resolved.entry;
  const existingResource = resolved.resourceId
    ? await input.db.resource.findUnique({
        where: { id: resolved.resourceId },
        select: { id: true, lcrCents: true, availability: true },
      })
    : null;

  const newHoursPerDay = input.hoursPerDay ?? existing.hoursPerDay;
  const newStartDate = input.startDate ?? existing.startDate;
  const newEndDate = input.endDate ?? existing.endDate;

  assertTimelineDateRangeValid(newStartDate, newEndDate);
  const { metadata: newMeta, includeSaturday } = buildTimelineAllocationMetadata({
    existingMetadata: existing.metadata as Record<string, unknown> | null | undefined,
    includeSaturday: input.includeSaturday,
  });

  let newDailyCostCents = 0;
  if (resolved.resourceId) {
    if (!existingResource) {
      throw new TRPCError({ code: "NOT_FOUND", message: "Resource not found" });
    }

    const availability = existingResource.availability as unknown as WeekdayAvailability;
    const recurrence = newMeta.recurrence as RecurrencePattern | undefined;
    newDailyCostCents = await calculateTimelineAllocationDailyCost({
      db: input.db,
      resourceId: resolved.resourceId,
      lcrCents: existingResource.lcrCents,
      hoursPerDay: newHoursPerDay,
      startDate: newStartDate,
      endDate: newEndDate,
      availability,
      includeSaturday,
      ...(recurrence ? { recurrence } : {}),
    });
  }

  return input.db.$transaction(async (tx) => {
    const { allocation: updatedAllocation } = await updateAllocationEntry(
      tx as unknown as Parameters<typeof updateAllocationEntry>[0],
      {
        id: input.allocationId,
        ...buildTimelineAllocationEntryUpdate({
          hoursPerDay: newHoursPerDay,
          startDate: newStartDate,
          endDate: newEndDate,
          metadata: newMeta,
          dailyCostCents: newDailyCostCents,
          role: input.role,
        }),
      },
    );

    await tx.auditLog.create({
      data: {
        entityType: "Allocation",
        entityId: input.allocationId,
        action: "UPDATE",
        changes: buildTimelineAllocationUpdateAuditChanges({
          allocationId: resolved.entry.id,
          previousHoursPerDay: existing.hoursPerDay,
          previousStartDate: existing.startDate,
          previousEndDate: existing.endDate,
          nextAllocationId: updatedAllocation.id,
          nextHoursPerDay: newHoursPerDay,
          nextStartDate: newStartDate,
          nextEndDate: newEndDate,
          includeSaturday,
        }),
      },
    });

    return updatedAllocation;
  });
}
