/**
 * rbac-matrix — pure-layer tests (wildcard permission matching + projection).
 */
import { describe, it, expect } from 'vitest';
import {
  permissionMatches,
  accessFor,
  buildAccessMap,
  ANONYMOUS_ROLE,
  type GrantedPermission,
} from '../rbac-matrix.js';

const exact = (path: string): GrantedPermission => ({ path, isWildcard: false });
const wild = (path: string): GrantedPermission => ({ path, isWildcard: true });

describe('permissionMatches', () => {
  it('exact grant matches only the identical permission', () => {
    expect(permissionMatches(exact('administration.users.read'), 'administration.users.read')).toBe(true);
    expect(permissionMatches(exact('administration.users.read'), 'administration.users.write')).toBe(false);
    // exact grant does NOT cover a parent or a child
    expect(permissionMatches(exact('administration.users'), 'administration.users.read')).toBe(false);
    expect(permissionMatches(exact('administration.users.read'), 'administration.users')).toBe(false);
  });

  it('wildcard grant covers itself and any descendant', () => {
    expect(permissionMatches(wild('administration.users'), 'administration.users')).toBe(true);
    expect(permissionMatches(wild('administration.users'), 'administration.users.read')).toBe(true);
    expect(permissionMatches(wild('administration.users'), 'administration.users.delete')).toBe(true);
    // not a sibling subtree
    expect(permissionMatches(wild('administration.users'), 'administration.roles.read')).toBe(false);
    // grant deeper than requirement cannot match
    expect(permissionMatches(wild('administration.users.read'), 'administration.users')).toBe(false);
  });

  it('a trailing-star wildcard normalises to its prefix', () => {
    expect(permissionMatches(wild('administration.*'), 'administration.users.read')).toBe(true);
    expect(permissionMatches(wild('administration.*'), 'billing.invoices.read')).toBe(false);
  });

  it('an empty grant path never matches', () => {
    expect(permissionMatches(wild('*'), 'administration.users.read')).toBe(false);
    expect(permissionMatches(exact(''), 'administration.users.read')).toBe(false);
  });
});

describe('accessFor', () => {
  it('anonymous is always redirect_login', () => {
    expect(accessFor(ANONYMOUS_ROLE, 'administration.users.read', [wild('administration')])).toBe('redirect_login');
    expect(accessFor(ANONYMOUS_ROLE, undefined, [])).toBe('redirect_login');
  });

  it('no required permission => allowed for any authenticated role', () => {
    expect(accessFor('viewer', undefined, [])).toBe('allowed');
  });

  it('allowed when a grant matches, denied otherwise', () => {
    expect(accessFor('admin', 'administration.users.read', [wild('administration.users')])).toBe('allowed');
    expect(accessFor('viewer', 'administration.users.read', [exact('billing.invoices.read')])).toBe('denied');
    expect(accessFor('viewer', 'administration.users.read', [])).toBe('denied');
  });
});

describe('buildAccessMap', () => {
  it('produces a verdict for every role, defaulting missing roles to no grants', () => {
    const roles = ['super-admin', 'admin', 'viewer', 'anonymous'];
    const grants = {
      'super-admin': [wild('administration')],
      admin: [wild('administration.users')],
      // viewer absent on purpose
    };
    const map = buildAccessMap(roles, 'administration.users.read', grants);
    expect(map).toEqual({
      'super-admin': 'allowed',
      admin: 'allowed',
      viewer: 'denied',
      anonymous: 'redirect_login',
    });
  });
});
