import { Prisma } from '@prisma/client';

/** Normalize the JSON `teeth` column of a lab case task into a clean string[]. */
export function normalizeTaskTeeth(value: Prisma.JsonValue | null | undefined): string[] {
  if (!Array.isArray(value)) {
    return [];
  }
  return value
    .filter((v): v is string | number => typeof v === 'string' || typeof v === 'number')
    .map((v) => String(v));
}

export function sortProsthesisTeeth(teeth: readonly string[]): string[] {
  return [...teeth].sort((a, b) => {
    const rank = (tooth: string) => (tooth === 'UA' ? 0 : tooth === 'LA' ? 1 : 2);
    const diff = rank(a) - rank(b);
    if (diff !== 0) return diff;
    return a.localeCompare(b, undefined, { numeric: true });
  });
}

/** Group ids that span more than one tooth (bridges). */
export function connectedSelectionGroupIds(
  rows: Array<{
    selectionGroupId?: string | null;
    tooth?: string;
    teeth?: Prisma.JsonValue;
  }>,
): Set<string> {
  const byGroup = new Map<string, Set<string>>();
  for (const row of rows) {
    const id = row.selectionGroupId?.trim();
    if (!id) continue;
    const teeth =
      row.tooth != null && row.tooth !== ''
        ? [row.tooth]
        : normalizeTaskTeeth(row.teeth);
    const set = byGroup.get(id) ?? new Set<string>();
    for (const tooth of teeth) set.add(tooth);
    byGroup.set(id, set);
  }
  const connected = new Set<string>();
  for (const [id, teeth] of byGroup) {
    if (teeth.size > 1) connected.add(id);
  }
  return connected;
}

export function aggregateProsthesisGroupsFromTasks(
  tasks: Array<{
    prosthesisTypeCode: string;
    teeth: Prisma.JsonValue;
    selectionGroupId?: string | null;
  }>,
): Array<{
  prosthesisTypeCode: string;
  teeth: string[];
  connected?: boolean;
  selectionGroupId?: string;
}> {
  const connectedIds = connectedSelectionGroupIds(tasks);
  const byKey = new Map<
    string,
    { prosthesisTypeCode: string; teeth: string[]; selectionGroupId: string }
  >();

  for (const task of tasks) {
    if (!task.prosthesisTypeCode) continue;
    const selectionGroupId = task.selectionGroupId?.trim() ?? '';
    const teeth = normalizeTaskTeeth(task.teeth);
    const key = `${teeth.join(',')}::${task.prosthesisTypeCode}`;
    const entry = byKey.get(key) ?? {
      prosthesisTypeCode: task.prosthesisTypeCode,
      teeth: [],
      selectionGroupId,
    };
    entry.teeth.push(...teeth);
    if (!entry.selectionGroupId && selectionGroupId) {
      entry.selectionGroupId = selectionGroupId;
    }
    byKey.set(key, entry);
  }

  return [...byKey.values()]
    .map((group) => {
      const teeth = sortProsthesisTeeth([...new Set(group.teeth)]);
      return {
        prosthesisTypeCode: group.prosthesisTypeCode,
        teeth,
        selectionGroupId: group.selectionGroupId || undefined,
        connected: Boolean(group.selectionGroupId) && connectedIds.has(group.selectionGroupId),
      };
    })
    .sort((a, b) => a.prosthesisTypeCode.localeCompare(b.prosthesisTypeCode));
}
