import { CatalogEntityKind, LabTaskStatus, Prisma } from '@prisma/client';
import { normalizeCatalogLocale } from '../catalog/catalog-label.service';

type TransactionClient = Prisma.TransactionClient;

export async function generateLabCaseTasks(
  tx: TransactionClient,
  labCaseId: string,
  localeInput?: string | null,
): Promise<number> {
  const existingCount = await tx.labCaseTask.count({ where: { labCaseId } });
  if (existingCount > 0) {
    return 0;
  }

  const locale = normalizeCatalogLocale(localeInput);

  const toothProsthesisRows = await tx.labCaseToothProsthesis.findMany({
    where: { labCaseId },
    include: {
      detail: { select: { id: true, treatmentType: true, toothSelectionGroups: true } },
    },
  });

  if (toothProsthesisRows.length === 0) {
    return 0;
  }

  const prosthesisCodes = [...new Set(toothProsthesisRows.map((r) => r.prosthesisTypeCode))];

  const prosthesisTypes = await tx.prosthesisType.findMany({
    where: { code: { in: prosthesisCodes }, isActive: true },
    include: {
      steps: {
        orderBy: { stepOrder: 'asc' },
        include: { labWorkflowStep: { select: { code: true } } },
      },
    },
  });

  const stepsByProsthesisCode = new Map(
    prosthesisTypes.map((type) => [
      type.code,
      type.steps.map((s) => ({
        stepOrder: s.stepOrder,
        workflowStepCode: s.labWorkflowStep.code,
      })),
    ]),
  );

  const allStepCodes = [
    ...new Set(
      prosthesisTypes.flatMap((type) =>
        type.steps.map((s) => s.labWorkflowStep.code),
      ),
    ),
  ];

  const stepLabels = await resolveStepLabels(tx, allStepCodes, locale);

  // Merge all teeth that share the same prosthesis type on a detail into one
  // task set (pre–selection-group behavior). selectionGroupId is stored for
  // UI/history only — not part of the grouping key.
  const groups = new Map<
    string,
    {
      treatmentDetailId: string;
      treatmentType: string;
      prosthesisTypeCode: string;
      selectionGroupId: string;
      teeth: string[];
    }
  >();

  for (const row of toothProsthesisRows) {
    const key = `${row.treatmentDetailId}::${row.prosthesisTypeCode}`;
    const selectionGroupId = row.selectionGroupId?.trim() || '';
    const group = groups.get(key) ?? {
      treatmentDetailId: row.treatmentDetailId,
      treatmentType: row.detail.treatmentType,
      prosthesisTypeCode: row.prosthesisTypeCode,
      selectionGroupId,
      teeth: [],
    };
    // Keep first non-empty selectionGroupId for persistence; grouping ignores it.
    if (!group.selectionGroupId && selectionGroupId) {
      group.selectionGroupId = selectionGroupId;
    }
    group.teeth.push(row.tooth);
    groups.set(key, group);
  }

  const taskRows: Prisma.LabCaseTaskCreateManyInput[] = [];

  for (const group of groups.values()) {
    const typeSteps = stepsByProsthesisCode.get(group.prosthesisTypeCode) ?? [];
    if (typeSteps.length === 0) {
      continue;
    }

    const teeth = sortTeeth(group.teeth);

    for (const step of typeSteps) {
      taskRows.push({
        labCaseId,
        treatmentDetailId: group.treatmentDetailId,
        teeth,
        treatmentType: group.treatmentType,
        prosthesisTypeCode: group.prosthesisTypeCode,
        selectionGroupId: group.selectionGroupId,
        workflowStepCode: step.workflowStepCode,
        stepOrder: step.stepOrder,
        stepLabel: stepLabels.get(step.workflowStepCode) ?? step.workflowStepCode,
        status: LabTaskStatus.IN_PROGRESS,
      });
    }
  }

  if (taskRows.length === 0) {
    return 0;
  }

  await tx.labCaseTask.createMany({ data: taskRows });
  return taskRows.length;
}

function sortTeeth(teeth: string[]): string[] {
  return [...new Set(teeth)].sort((a, b) => {
    const na = Number(a);
    const nb = Number(b);
    if (!Number.isNaN(na) && !Number.isNaN(nb)) return na - nb;
    return a.localeCompare(b);
  });
}

async function resolveStepLabels(
  tx: TransactionClient,
  stepCodes: string[],
  locale: string,
): Promise<Map<string, string>> {
  if (stepCodes.length === 0) {
    return new Map();
  }

  const rows = await tx.catalogTranslation.findMany({
    where: {
      entityKind: CatalogEntityKind.LAB_WORKFLOW_STEP,
      entityCode: { in: stepCodes },
      locale: { in: [locale, 'en'] },
    },
    select: { entityCode: true, locale: true, label: true },
  });

  const byCode = new Map<string, { en?: string; locale?: string }>();
  for (const row of rows) {
    const entry = byCode.get(row.entityCode) ?? {};
    if (row.locale === 'en') entry.en = row.label;
    if (row.locale === locale) entry.locale = row.label;
    byCode.set(row.entityCode, entry);
  }

  const result = new Map<string, string>();
  for (const code of stepCodes) {
    const entry = byCode.get(code);
    result.set(code, entry?.locale ?? entry?.en ?? code);
  }
  return result;
}
