/**
 * The policy role picker — grouping, selection and toggling.
 *
 * Exercises the frontend module directly (it is dependency-free for exactly
 * this reason). The case that matters is the one that shipped broken: the
 * Super Admin endpoint returns every Role document, including the per-company
 * copy of each default role, so a platform with a dozen companies produced a
 * dozen identical "Manager" chips.
 */
import path from 'path';

const ROOT = path.resolve(__dirname, '../../../../..');
/* eslint-disable @typescript-eslint/no-var-requires */
const { groupRoles, isRoleSelected, toggleRole } =
  require(`${ROOT}/src/frontend/src/components/security/roleGrouping`);

let fail = 0;
const check = (ok: boolean, label: string) => { if (!ok) fail++; console.log(`${ok ? 'PASS' : 'FAIL'}  ${label}`); };

const oid = (s: string) => s.padEnd(24, '0');

/** What GET /roles actually answers with on a platform of many companies. */
function realWorldRoleDocuments(companyCount: number) {
  const docs: any[] = [
    { _id: oid('gmanager'), name: 'manager', displayName: 'Manager', scope: 'global' },
    { _id: oid('gadmin'), name: 'admin', displayName: 'Admin', scope: 'global' },
    { _id: oid('gviewer'), name: 'viewer', displayName: 'Viewer', scope: 'global' },
    { _id: oid('gsuper'), name: 'super-admin', displayName: 'Super Admin', scope: 'global' },
  ];
  for (let i = 0; i < companyCount; i++) {
    docs.push(
      { _id: oid(`c${i}mgr`), name: 'manager', displayName: 'Manager', scope: `c${i}`, isOrgDefault: true },
      { _id: oid(`c${i}adm`), name: 'admin', displayName: 'Admin', scope: `c${i}`, isOrgDefault: true },
      { _id: oid(`c${i}vwr`), name: 'viewer', displayName: 'Viewer', scope: `c${i}`, isOrgDefault: true },
    );
  }
  return docs;
}

// ── 1. The reported bug ──────────────────────────────────────────────────────
console.log('\n-- duplicate chips --');

const raw = realWorldRoleDocuments(12);
check(raw.filter(r => r.name === 'manager').length === 13,
  'the endpoint really does return 13 manager documents for 12 companies');

// The fix that was asked for, demonstrated not to work:
const byId = Array.from(new Map(raw.map(r => [String(r._id), r])).values());
check(byId.filter(r => r.name === 'manager').length === 13,
  'deduplicating by id removes NOTHING — every copy is a distinct document');

const grouped = groupRoles(raw);
check(grouped.filter(r => r.name === 'manager').length === 1, 'grouping by name gives one Manager entry');
check(grouped.length === 3, `one chip per logical role -> ${grouped.map((r: any) => r.displayName).join(', ')}`);

// ── 2. Canonical id ──────────────────────────────────────────────────────────
console.log('\n-- which id gets stored --');
const manager = grouped.find((r: any) => r.name === 'manager')!;
check(manager.id === oid('gmanager'), 'the GLOBAL document is the id a policy stores');
check(manager.ids.length === 13, 'but every copy is remembered behind the chip');

const orgOnly = groupRoles([
  { _id: oid('c1only'), name: 'abc', displayName: 'abc', scope: 'c1' },
]);
check(orgOnly[0].id === oid('c1only'), 'a role with no global copy uses its own document');

// ── 3. Exempt role ───────────────────────────────────────────────────────────
console.log('\n-- exempt role --');
check(!grouped.some((r: any) => r.name === 'super-admin'), 'Super Admin is never offered as a chip');

// ── 4. Selection across copies ───────────────────────────────────────────────
console.log('\n-- selection --');
check(isRoleSelected(manager, [oid('gmanager')]) === true, 'selected via the canonical id');
check(isRoleSelected(manager, [oid('c7mgr')]) === true,
  'ALSO selected when the policy holds a company copy id — otherwise the chip would look off '
  + 'and the next save would silently clear the policy');
check(isRoleSelected(manager, ['manager']) === true, 'and when the policy still stores the name (pre-migration)');
check(isRoleSelected(manager, [oid('gadmin')]) === false, 'not selected by an unrelated role');
check(isRoleSelected(manager, []) === false, 'not selected when nothing is targeted');

// ── 5. Toggling ──────────────────────────────────────────────────────────────
console.log('\n-- toggling --');
check(toggleRole(manager, []).join() === oid('gmanager'), 'turning on stores the canonical id');

const messy = ['manager', oid('c3mgr'), oid('gmanager'), oid('gadmin')];
const off = toggleRole(manager, messy);
check(off.length === 1 && off[0] === oid('gadmin'),
  'turning off removes the name AND every copy id, leaving other roles alone');

const on = toggleRole(manager, [oid('gadmin')]);
check(on.length === 2 && on.includes(oid('gadmin')) && on.includes(oid('gmanager')),
  'turning on does not disturb an existing selection');

// ── 6. Ordering and junk input ───────────────────────────────────────────────
console.log('\n-- ordering and junk --');
check(grouped.map((r: any) => r.displayName).join() === 'Admin,Manager,Viewer', 'entries come out sorted by label');
check(groupRoles([]).length === 0, 'no roles yields no chips');
check(groupRoles(null as any).length === 0, 'a null payload does not throw');
check(groupRoles([{ name: 'x' }, { _id: oid('y') }] as any).length === 0,
  'documents missing an id or a name are skipped rather than rendered blank');
check(groupRoles([{ _id: oid('n'), name: 'newrole', scope: 'c1' }] as any)[0].displayName === 'newrole',
  'a role with no displayName falls back to its name');

console.log(fail === 0 ? '\nAll checks passed.' : `\n${fail} check(s) FAILED.`);
process.exit(fail ? 1 : 0);
