import type { Prisma } from '../../src/generated/prisma/client';

export function seedWorkingGroupPrefix(group: {
  id: number;
  khName: string | null;
  description: string | null;
}) {
  const letters = 'កខគឃងចឆជឈញដឋឌឍណត';
  const khLetter = group.khName?.match(
    /ក្រុមការងារ\s*([កខគឃងចឆជឈញដឋឌឍណត])/u,
  )?.[1];
  const enLetter = group.description
    ?.match(/\bWG\s+([A-P])\b/i)?.[1]
    ?.toUpperCase();
  const groupNumber = khLetter
    ? letters.indexOf(khLetter) + 1
    : enLetter
      ? enLetter.charCodeAt(0) - 64
      : 0;
  if (!groupNumber)
    throw new Error(`Missing working-group letter for stakeholder ${group.id}`);
  return `WG${String(groupNumber).padStart(2, '0')}_`;
}

// The caller must lock the issues table for the duration of this transaction.
export async function backfillSeedIssueNumbers(
  tx: Prisma.TransactionClient,
  eligibleIssueIds?: number[],
) {
  const eligible =
    eligibleIssueIds === undefined ? undefined : new Set(eligibleIssueIds);
  if (eligible?.size === 0) return 0;
  const issues = await tx.issues.findMany({
    orderBy: [{ createdAt: 'asc' }, { id: 'asc' }],
    select: {
      id: true,
      numberIssue: true,
      updatedAt: true,
      stakeholder: { select: { id: true, khName: true, description: true } },
    },
  });
  const highestByPrefix = new Map<string, number>();
  const ownerByPrefix = new Map<string, number>();
  const rows = issues.map((issue) => {
    const group = issue.stakeholder;
    const prefix = seedWorkingGroupPrefix(group);
    const owner = ownerByPrefix.get(prefix);
    if (owner !== undefined && owner !== group.id)
      throw new Error(`Duplicate working-group prefix ${prefix}`);
    ownerByPrefix.set(prefix, group.id);
    if (issue.numberIssue !== null) {
      const suffix = issue.numberIssue.slice(prefix.length);
      if (!issue.numberIssue.startsWith(prefix) || !/^\d+$/.test(suffix))
        throw new Error(`Unexpected number on issue ${issue.id}`);
      highestByPrefix.set(
        prefix,
        Math.max(highestByPrefix.get(prefix) ?? 0, Number(suffix)),
      );
    }
    return { ...issue, prefix };
  });
  let updated = 0;
  for (const issue of rows) {
    if (issue.numberIssue !== null || (eligible && !eligible.has(issue.id)))
      continue;
    const next = (highestByPrefix.get(issue.prefix) ?? 0) + 1;
    highestByPrefix.set(issue.prefix, next);
    await tx.issues.update({
      where: { id: issue.id },
      data: {
        numberIssue: `${issue.prefix}${String(next).padStart(4, '0')}`,
        updatedAt: issue.updatedAt,
      },
    });
    updated++;
  }
  return updated;
}
