import type {
  DashboardSummary,
  DashboardStatusCount,
} from "../dashboard/service/cdc-gpsf-dashboard-service";

function groupBy<T>(items: T[], getId: (item: T) => number | string) {
  const groups = new Map<number | string, T[]>();
  for (const item of items) {
    const id = getId(item);
    const group = groups.get(id);
    if (group) group.push(item);
    else groups.set(id, [item]);
  }
  return [...groups.values()];
}

function combineStatuses(items: DashboardStatusCount[]) {
  return groupBy(items, (item) => item.code).map((group) => ({
    ...group[0],
    count: group.reduce((sum, item) => sum + item.count, 0),
  }));
}

export function combineSummaries(
  sources: DashboardSummary[],
): DashboardSummary {
  const byPrimaryAgency = groupBy(
    sources.flatMap((s) => s.byPrimaryAgency),
    (a) => a.agencyId,
  ).map((group) => ({
    ...group[0],
    total: group.reduce((sum, a) => sum + a.total, 0),
    byStatus: combineStatuses(group.flatMap((a) => a.byStatus)),
  }));
  return {
    // Count records from their source totals, never from overlapping group memberships.
    cards: {
      totalIssues: sources.reduce((sum, s) => sum + s.cards.totalIssues, 0),
      solved: sources.reduce((sum, s) => sum + s.cards.solved, 0),
      inProgress: sources.reduce((sum, s) => sum + s.cards.inProgress, 0),
      notAddressed: sources.reduce((sum, s) => sum + s.cards.notAddressed, 0),
      totalPrimaryAgencies: byPrimaryAgency.filter((a) => a.total > 0).length,
    },
    byPrimaryAgency,
    statusBreakdown: combineStatuses(sources.flatMap((s) => s.statusBreakdown)),
    byWorkingGroup: groupBy(
      sources.flatMap((s) => s.byWorkingGroup),
      (g) => g.workingGroupId,
    ).map((group) => ({
      ...group[0],
      total: group.reduce((sum, g) => sum + g.total, 0),
      byStatus: combineStatuses(group.flatMap((g) => g.byStatus)),
    })),
    byCategory: groupBy(
      sources.flatMap((s) => s.byCategory),
      (c) => c.categoryId,
    ).map((group) => ({
      ...group[0],
      count: group.reduce((sum, c) => sum + c.count, 0),
      byStatus: combineStatuses(group.flatMap((c) => c.byStatus ?? [])),
    })),
  };
}
