import type {
  AllocationLike,
  AllocationReadModel,
  Assignment,
  DemandRequirement,
} from "@capakraken/shared";
type SplitAllocationEntry = AllocationLike;
type SplitProjectSummary = NonNullable<SplitAllocationEntry["project"]>;
type SplitResourceSummary = NonNullable<SplitAllocationEntry["resource"]>;
type SplitRoleSummary = NonNullable<SplitAllocationEntry["roleEntity"]>;
type SplitDemandAllocationEntry = SplitAllocationEntry & {
  resourceId: null;
  isPlaceholder: true;
};
type SplitAssignmentAllocationEntry = SplitAllocationEntry & {
  resourceId: string;
  isPlaceholder: false;
};

export interface SplitDemandRequirementRecord {
  id: string;
  projectId: string;
  startDate: Date | string;
  endDate: Date | string;
  hoursPerDay: number;
  percentage: number;
  role: string | null;
  roleId: string | null;
  headcount: number;
  status: string;
  metadata: unknown;
  createdAt: Date | string;
  updatedAt: Date | string;
  project?: SplitProjectSummary;
  roleEntity?: SplitRoleSummary | null;
}

export interface SplitAssignmentRecord {
  id: string;
  demandRequirementId?: string | null;
  resourceId: string;
  projectId: string;
  startDate: Date | string;
  endDate: Date | string;
  hoursPerDay: number;
  percentage: number;
  role: string | null;
  roleId: string | null;
  dailyCostCents: number;
  status: string;
  metadata: unknown;
  createdAt: Date | string;
  updatedAt: Date | string;
  resource?: SplitResourceSummary | null;
  project?: SplitProjectSummary;
  roleEntity?: SplitRoleSummary | null;
}

export interface BuildSplitAllocationReadModelInput {
  demandRequirements: SplitDemandRequirementRecord[];
  assignments: SplitAssignmentRecord[];
}

function compareEntries(
  left: Pick<SplitAllocationEntry, "startDate" | "resourceId" | "id">,
  right: Pick<SplitAllocationEntry, "startDate" | "resourceId" | "id">,
): number {
  const startDelta =
    new Date(left.startDate).getTime() - new Date(right.startDate).getTime();
  if (startDelta !== 0) {
    return startDelta;
  }

  const resourceDelta = (left.resourceId ?? "").localeCompare(right.resourceId ?? "");
  if (resourceDelta !== 0) {
    return resourceDelta;
  }

  return left.id.localeCompare(right.id);
}

function toDemandAllocationEntry(
  demandRequirement: SplitDemandRequirementRecord,
): SplitDemandAllocationEntry {
  return {
    id: demandRequirement.id,
    entityId: demandRequirement.id,
    resourceId: null,
    projectId: demandRequirement.projectId,
    startDate: demandRequirement.startDate,
    endDate: demandRequirement.endDate,
    hoursPerDay: demandRequirement.hoursPerDay,
    percentage: demandRequirement.percentage,
    role: demandRequirement.role,
    roleId: demandRequirement.roleId,
    isPlaceholder: true,
    headcount: demandRequirement.headcount,
    dailyCostCents: 0,
    status: demandRequirement.status,
    metadata: demandRequirement.metadata,
    createdAt: demandRequirement.createdAt,
    updatedAt: demandRequirement.updatedAt,
    ...(demandRequirement.project ? { project: demandRequirement.project } : {}),
    ...(demandRequirement.roleEntity !== undefined
      ? { roleEntity: demandRequirement.roleEntity ?? null }
      : {}),
  };
}

function toAssignmentAllocationEntry(
  assignment: SplitAssignmentRecord,
): SplitAssignmentAllocationEntry {
  return {
    id: assignment.id,
    entityId: assignment.id,
    resourceId: assignment.resourceId,
    projectId: assignment.projectId,
    startDate: assignment.startDate,
    endDate: assignment.endDate,
    hoursPerDay: assignment.hoursPerDay,
    percentage: assignment.percentage,
    role: assignment.role,
    roleId: assignment.roleId,
    isPlaceholder: false,
    headcount: 1,
    dailyCostCents: assignment.dailyCostCents,
    status: assignment.status,
    metadata: assignment.metadata,
    createdAt: assignment.createdAt,
    updatedAt: assignment.updatedAt,
    ...(assignment.resource !== undefined ? { resource: assignment.resource ?? null } : {}),
    ...(assignment.project ? { project: assignment.project } : {}),
    ...(assignment.roleEntity !== undefined ? { roleEntity: assignment.roleEntity ?? null } : {}),
  };
}

function toDemandReadModelEntry(
  demandRequirement: SplitDemandRequirementRecord,
): DemandRequirement<SplitAllocationEntry> {
  const entry = toDemandAllocationEntry(demandRequirement);

  return {
    ...entry,
    kind: "demand",
    sourceAllocationId: demandRequirement.id,
    requestedHeadcount: demandRequirement.headcount,
    unfilledHeadcount: demandRequirement.headcount,
  };
}

function toAssignmentReadModelEntry(
  assignment: SplitAssignmentRecord,
): Assignment<SplitAllocationEntry> {
  const entry = toAssignmentAllocationEntry(assignment);

  return {
    ...entry,
    kind: "assignment",
    sourceAllocationId: assignment.id,
  };
}

export function buildSplitAllocationReadModel({
  demandRequirements,
  assignments,
}: BuildSplitAllocationReadModelInput): AllocationReadModel<SplitAllocationEntry> {
  return {
    allocations: [
      ...demandRequirements.map(toDemandAllocationEntry),
      ...assignments.map(toAssignmentAllocationEntry),
    ].sort(compareEntries),
    demands: demandRequirements.map(toDemandReadModelEntry).sort(compareEntries),
    assignments: assignments.map(toAssignmentReadModelEntry).sort(compareEntries),
  };
}
