/**
 * Backfill the walk-in QR permissions for admins who predate them.
 *
 * Context. Before the walk-in QR permission split, creating / editing /
 * deleting a walk-in QR required canManageSettings — every write route was
 * gated by requireFlag('canManageSettings'). The split introduced three
 * dedicated flags (canViewWalkInQr / canAddWalkInQr / canDeleteWalkInQr),
 * each defaulting to false, and restored access in migration SQL.
 *
 * Two gaps that left admins without the "New QR" button they used to have:
 *   1. The migration backfilled from the visitor flags (canAddVisitors /
 *      canDeleteVisitors) rather than the gate that was actually in force,
 *      canManageSettings.
 *   2. Environments whose schema is applied with `prisma db push` never run
 *      migration SQL at all, so no backfill happened there.
 *
 * This script closes both. It only ever grants — nothing is revoked — so it
 * is safe to re-run, and safe on an environment where the migrations did
 * apply cleanly.
 *
 * Dry run (default):  npx ts-node -r dotenv/config src/scripts/backfillWalkInQrPermissions.ts
 * Apply:              npx ts-node -r dotenv/config src/scripts/backfillWalkInQrPermissions.ts --apply
 */

import { prisma } from '../config/database';

const APPLY = process.argv.includes('--apply');

async function main(): Promise<void> {
  const admins = await prisma.admin.findMany({
    orderBy: { email: 'asc' },
    select: {
      id: true,
      email: true,
      createdByAdminId: true,
      canManageSettings: true,
      canViewWalkInQr: true,
      canAddWalkInQr: true,
      canDeleteWalkInQr: true,
    },
  });

  type Flags = { canViewWalkInQr: boolean; canAddWalkInQr: boolean; canDeleteWalkInQr: boolean };

  // Pass 1 — what the legacy gate entitles each admin to, on their own.
  const granted = new Map<string, Flags>();
  for (const a of admins) {
    // The real legacy gate: canManageSettings meant "can create and delete
    // walk-in QRs".
    const add = a.canAddWalkInQr || a.canManageSettings;
    const del = a.canDeleteWalkInQr || a.canManageSettings;
    granted.set(a.id, {
      canAddWalkInQr: add,
      canDeleteWalkInQr: del,
      // Anyone who can add or delete has to be able to reach the section.
      canViewWalkInQr: a.canViewWalkInQr || add || del,
    });
  }

  // Pass 2 — a sub-admin can never hold more than the admin who created
  // them (same rule createAdmin/updateAdmin enforce on every write), so
  // clamp each receptionist to their parent's post-backfill set.
  for (const a of admins) {
    if (!a.createdByAdminId) continue;
    const parent = granted.get(a.createdByAdminId);
    if (!parent) continue;
    const own = granted.get(a.id)!;
    granted.set(a.id, {
      canAddWalkInQr: own.canAddWalkInQr && parent.canAddWalkInQr,
      canDeleteWalkInQr: own.canDeleteWalkInQr && parent.canDeleteWalkInQr,
      canViewWalkInQr: own.canViewWalkInQr && parent.canViewWalkInQr,
    });
  }

  const changes: { id: string; email: string; data: Record<string, boolean>; summary: string }[] = [];

  for (const a of admins) {
    const target = granted.get(a.id)!;
    const data: Record<string, boolean> = {};
    // Grants only — a flag that is already on is never turned back off.
    if (target.canAddWalkInQr && !a.canAddWalkInQr) data.canAddWalkInQr = true;
    if (target.canDeleteWalkInQr && !a.canDeleteWalkInQr) data.canDeleteWalkInQr = true;
    if (target.canViewWalkInQr && !a.canViewWalkInQr) data.canViewWalkInQr = true;

    if (Object.keys(data).length > 0) {
      changes.push({ id: a.id, email: a.email, data, summary: Object.keys(data).join(', ') });
    }
  }

  console.log(`[walkin-qr] ${admins.length} admins scanned, ${changes.length} need a grant`);
  for (const c of changes) console.log(`[walkin-qr] ${c.email}: grant ${c.summary}`);

  if (!APPLY) {
    console.log('[walkin-qr] dry run — re-run with --apply to write these changes');
    return;
  }

  for (const c of changes) {
    await prisma.admin.update({ where: { id: c.id }, data: c.data });
  }
  console.log(`[walkin-qr] applied ${changes.length} update(s)`);
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
