import { strict as assert } from 'node:assert';
import { describe, it } from 'node:test';
import { TEAM_ROLES, TEAMS, type Team, type TeamRole } from '../src/ontology';
import {
  CAPABILITIES,
  permissionGranted,
  resolvePermissionGrants,
  resolveReadPermissionGrants,
  resolveWritePermissionGrants,
  roleMeets,
  type Capability,
  type PermissionSubject,
} from '../src/permissions';

describe('role permissions', () => {
  it('keeps deal writes on the side where the person is a member', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'demand', role: 'member' }],
    });

    assert.equal(permissionGranted(grants, 'deal:write', 'demand'), true);
    assert.equal(permissionGranted(grants, 'deal:write', 'supply'), false);
    assert.equal(permissionGranted(grants, 'commitment:write', 'demand'), false);
  });

  it('allows supply leads to commit capacity without letting them sign contracts', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'supply', role: 'lead' }],
    });

    assert.equal(permissionGranted(grants, 'commitment:write', 'supply'), true);
    assert.equal(permissionGranted(grants, 'contract:sign', 'supply'), false);
  });

  it('keeps signing and bulk import at team-admin level', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'demand', role: 'admin' }],
    });

    assert.equal(permissionGranted(grants, 'contract:sign', 'demand'), true);
    assert.equal(permissionGranted(grants, 'contract:sign', 'supply'), false);
    assert.equal(permissionGranted(grants, 'data:import', 'demand'), true);
    assert.equal(permissionGranted(grants, 'data:import', 'research'), false);
    assert.equal(permissionGranted(grants, 'settings:admin'), false);
  });

  it('gives platform admins global grants without synthetic team memberships', () => {
    const grants = resolvePermissionGrants({ isPlatformAdmin: true, teams: [] });

    assert.equal(permissionGranted(grants, 'deal:write', 'demand'), true);
    assert.equal(permissionGranted(grants, 'commitment:write', 'supply'), true);
    assert.equal(permissionGranted(grants, 'data:import', 'research'), true);
    assert.equal(permissionGranted(grants, 'settings:admin'), true);
  });
});

describe('the three authorities that used to be data:import', () => {
  it('does not let a research admin rewrite a commercial book', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'research', role: 'admin' }],
    });

    assert.equal(permissionGranted(grants, 'fact:review', 'research'), true);
    assert.equal(permissionGranted(grants, 'data:import', 'demand'), false);
    assert.equal(permissionGranted(grants, 'data:import', 'supply'), false);
  });

  it('does not let a commercial admin approve a claim about a person', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'supply', role: 'admin' }],
    });

    assert.equal(permissionGranted(grants, 'data:import', 'supply'), true);
    assert.equal(permissionGranted(grants, 'integration:connect', 'supply'), true);
    // Fact review lives on research alone; being a supply admin buys nothing.
    assert.equal(permissionGranted(grants, 'fact:review', 'research'), false);
  });
});

describe('reads', () => {
  it('resolves read grants platform-wide, never per team', () => {
    const grants = resolveReadPermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'demand', role: 'member' }],
    });

    // A team-scoped read grant would be a promise the query layer does not
    // keep: `/api/contracts` returns supply paper to a demand reader either
    // way. See READ_CAPABILITIES.
    assert.deepEqual(
      grants,
      [
        { capability: 'book:read', team: null },
        { capability: 'economics:read', team: null },
        { capability: 'team:read', team: null },
      ],
    );
  });

  it('shows a research contractor the book but not what we pay for capacity', () => {
    const grants = resolveReadPermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'research', role: 'lead' }],
    });

    assert.equal(permissionGranted(grants, 'book:read'), true);
    assert.equal(permissionGranted(grants, 'team:read'), true);
    assert.equal(permissionGranted(grants, 'economics:read'), false);
  });

  it('gives a viewer reads and no writes at all', () => {
    const subject: PermissionSubject = {
      isPlatformAdmin: false,
      teams: [{ team: 'demand', role: 'viewer' }],
    };

    assert.deepEqual(resolveWritePermissionGrants(subject), []);
    assert.equal(permissionGranted(resolveReadPermissionGrants(subject), 'book:read'), true);
    // Cost economics are a commercial member's tool, not a reader's.
    assert.equal(permissionGranted(resolveReadPermissionGrants(subject), 'economics:read'), false);
  });
});

/**
 * The matrix is pure data, so pinning every cell is cheap — and it is the only
 * way a role added later cannot quietly inherit an authority nobody chose to
 * give it. Change a rule and this table tells you exactly which cells moved.
 */
describe('the whole role × capability matrix', () => {
  const EXPECTED: Readonly<Record<TeamRole, readonly Capability[]>> = {
    viewer: ['book:read', 'team:read'],
    member: [
      'book:read',
      'economics:read',
      'team:read',
      'deal:write',
      'activity:write',
      'motion:write',
    ],
    lead: [
      'book:read',
      'economics:read',
      'team:read',
      'deal:write',
      'activity:write',
      'commitment:write',
      'motion:write',
      'motion:publish',
    ],
    admin: [
      'book:read',
      'economics:read',
      'team:read',
      'deal:write',
      'activity:write',
      'commitment:write',
      'contract:sign',
      'data:import',
      'integration:connect',
      'motion:write',
      'motion:publish',
    ],
  };

  /** Held on the supply team, whose rules exercise every rank threshold. */
  for (const role of TEAM_ROLES) {
    it(`grants a supply ${role} exactly the expected set`, () => {
      const grants = resolvePermissionGrants({
        isPlatformAdmin: false,
        teams: [{ team: 'supply', role }],
      });
      const held = CAPABILITIES.filter((capability) =>
        grants.some((grant) => grant.capability === capability),
      );
      assert.deepEqual(new Set(held), new Set(EXPECTED[role]));
    });
  }

  it('gives research its own shape — evidence review, no commercial reach', () => {
    const grants = resolvePermissionGrants({
      isPlatformAdmin: false,
      teams: [{ team: 'research', role: 'admin' }],
    });
    const held = CAPABILITIES.filter((capability) =>
      grants.some((grant) => grant.capability === capability),
    );

    assert.deepEqual(
      new Set(held),
      new Set([
        'book:read',
        'team:read',
        'activity:write',
        'data:import',
        'fact:review',
        'integration:connect',
        'motion:write',
        'motion:publish',
      ]),
    );
  });

  it('never grants a lower rank something a higher rank on the same team lacks', () => {
    for (const team of TEAMS as readonly Team[]) {
      let previous = new Set<Capability>();
      for (const role of TEAM_ROLES) {
        const held = new Set<Capability>(
          resolvePermissionGrants({ isPlatformAdmin: false, teams: [{ team, role }] }).map(
            (grant) => grant.capability,
          ),
        );
        for (const capability of previous) {
          assert.ok(held.has(capability), `${team}/${role} lost ${capability} by promotion`);
        }
        previous = held;
      }
    }
  });

  it('ranks viewer below member, which is what makes it safe to add', () => {
    assert.equal(roleMeets('viewer', 'member'), false);
    assert.equal(roleMeets('member', 'viewer'), true);
    assert.equal(roleMeets('admin', 'admin'), true);
    assert.equal(TEAM_ROLES[0], 'viewer');
  });
});
