/**
 * uat-plan/project-roles — pure projection tests (role ordering, route expansion,
 * URL/strategy rules, RBAC access + endpoint status maps).
 */
import { describe, it, expect } from 'vitest';
import {
  canonicalRoleOrder,
  routeIdFor,
  componentKeyFor,
  urlForView,
  navStrategyForView,
  expandNavRoute,
  projectRoutes,
  projectEndpoints,
  expectedStatusByRole,
  okStatusFor,
} from '../project-roles.js';
import type { DiscoveredNavRoute, DiscoveredEndpoint } from '../types.js';

const GRANTS = {
  'super-admin': [{ path: 'administration', isWildcard: true }],
  admin: [{ path: 'administration.users', isWildcard: true }],
  viewer: [{ path: 'administration.users.read', isWildcard: false }],
};
const ROLES = ['super-admin', 'admin', 'viewer', 'anonymous'];

describe('canonicalRoleOrder', () => {
  it('orders known roles by privilege, anonymous last, and dedupes', () => {
    expect(canonicalRoleOrder(['viewer', 'anonymous', 'admin', 'super-admin', 'admin'])).toEqual([
      'super-admin',
      'admin',
      'viewer',
      'anonymous',
    ]);
  });

  it('sorts unknown roles by ASCII after the known ones', () => {
    expect(canonicalRoleOrder(['zulu', 'admin', 'alpha'])).toEqual(['admin', 'alpha', 'zulu']);
  });
});

describe('routeIdFor / componentKeyFor', () => {
  it('base views map to the bare UPPER_SNAKE key; suffix views append the view', () => {
    expect(routeIdFor('administration.users', 'list')).toBe('ADMINISTRATION_USERS');
    expect(routeIdFor('administration.users', 'section-home')).toBe('ADMINISTRATION_USERS');
    expect(routeIdFor('administration.users', 'detail')).toBe('ADMINISTRATION_USERS_DETAIL');
  });

  it('component key is the bare key for base views, suffixed otherwise', () => {
    expect(componentKeyFor('a.b', 'list')).toBe('a.b');
    expect(componentKeyFor('a.b', 'create')).toBe('a.b.create');
  });
});

describe('urlForView / navStrategyForView', () => {
  it('maps each view to the DynamicRouter URL convention (incl. id-bearing suffixes)', () => {
    expect(urlForView('/a/b', 'list')).toBe('/a/b');
    expect(urlForView('/a/b', 'detail')).toBe('/a/b/:id');
    expect(urlForView('/a/b', 'edit')).toBe('/a/b/:id/edit');
    expect(urlForView('/a/b', 'create')).toBe('/a/b/create');
    expect(urlForView('/a/b', 'settings')).toBe('/a/b/:id/settings');
    expect(urlForView('/a/b', 'duplicate')).toBe('/a/b/:id/duplicate');
    expect(urlForView('/a/b', 'import')).toBe('/a/b/import');
    expect(urlForView('/a/b', 'dashboard')).toBe('/a/b/dashboard'); // unknown → fallback
  });

  it('id-bearing pages click a row in the parent; lists use the menu; create/collection goto', () => {
    expect(navStrategyForView('detail')).toBe('click_row_in_parent');
    expect(navStrategyForView('edit')).toBe('click_row_in_parent');
    expect(navStrategyForView('settings')).toBe('click_row_in_parent'); // /:id/settings
    expect(navStrategyForView('list')).toBe('menu_click');
    expect(navStrategyForView('create')).toBe('goto');
    expect(navStrategyForView('import')).toBe('goto'); // collection-level
  });
});

describe('expandNavRoute', () => {
  const node: DiscoveredNavRoute = {
    componentKey: 'administration.users',
    baseRoute: '/administration/users',
    label: 'Users',
    permission: 'administration.users.read',
    navKind: 'section',
    views: ['list', 'detail'],
  };

  it('emits one route per view with the shared access map and route-level metadata', () => {
    const routes = expandNavRoute(node, ROLES, GRANTS);
    expect(routes.map((r) => r.id)).toEqual(['ADMINISTRATION_USERS', 'ADMINISTRATION_USERS_DETAIL']);

    const [list, detail] = routes;
    expect(list.access).toEqual({
      'super-admin': 'allowed',
      admin: 'allowed',
      viewer: 'allowed',
      anonymous: 'redirect_login',
    });
    expect(list.expect.title).toBe('Users');
    expect(list.subsets).toEqual([]);
    expect(list.permission).toBe('administration.users.read');

    expect(detail.navigation_strategy).toBe('click_row_in_parent');
    expect(detail.parent_route).toBe('/administration/users');
    expect(detail.on_empty_list).toBe('INDETERMINATE');
  });

  it('dedupes repeated views', () => {
    const dup = expandNavRoute({ ...node, views: ['list', 'list'] }, ROLES, GRANTS);
    expect(dup).toHaveLength(1);
  });

  it('omits permission + title when the node has none', () => {
    const bare = expandNavRoute(
      { componentKey: 'public.home', baseRoute: '/public/home', navKind: 'section', views: ['list'] },
      ['admin', 'anonymous'],
      {},
    );
    expect(bare[0].permission).toBeUndefined();
    expect(bare[0].expect.title).toBeUndefined();
    // no required permission => any authenticated role allowed
    expect(bare[0].access).toEqual({ admin: 'allowed', anonymous: 'redirect_login' });
  });
});

describe('projectRoutes', () => {
  it('flattens nodes in discovery order', () => {
    const nodes: DiscoveredNavRoute[] = [
      { componentKey: 'a.users', baseRoute: '/a/users', navKind: 'section', views: ['list', 'detail'] },
      { componentKey: 'a.roles', baseRoute: '/a/roles', navKind: 'section', views: ['list'] },
    ];
    expect(projectRoutes(nodes, ROLES, GRANTS).map((r) => r.id)).toEqual([
      'A_USERS',
      'A_USERS_DETAIL',
      'A_ROLES',
    ]);
  });
});

describe('expectedStatusByRole / projectEndpoints', () => {
  it('maps anonymous→401, granted→ok, denied→403', () => {
    expect(expectedStatusByRole(ROLES, 'administration.users.read', GRANTS, 200)).toEqual({
      'super-admin': 200,
      admin: 200,
      viewer: 200,
      anonymous: 401,
    });
    expect(expectedStatusByRole(['x', 'anonymous'], 'administration.users.read', {}, 200)).toEqual({
      x: 403,
      anonymous: 401,
    });
  });

  it('okStatusFor defaults to 200 for every verb', () => {
    expect(okStatusFor('POST')).toBe(200);
    expect(okStatusFor('DELETE')).toBe(200);
  });

  it('a wildcard grant covers a deeper create permission; success status honours okStatus (201)', () => {
    const eps: DiscoveredEndpoint[] = [
      {
        method: 'POST',
        route: '/api/administration/users',
        controller: 'UsersController',
        permission: 'administration.users.create',
        okStatus: 201,
      },
    ];
    const out = projectEndpoints(eps, ROLES, GRANTS);
    expect(out[0].id).toBe('post:administration/users');
    expect(out[0].controller).toBe('UsersController');
    expect(out[0].expected_by_role).toEqual({
      'super-admin': 201, // wildcard "administration"
      admin: 201, // wildcard "administration.users" covers .create
      viewer: 403, // exact "administration.users.read" only
      anonymous: 401,
    });
  });

  it('allowAnonymous endpoints give every role (incl. anonymous) the success status', () => {
    const eps: DiscoveredEndpoint[] = [{ method: 'GET', route: '/api/public/health', allowAnonymous: true }];
    const out = projectEndpoints(eps, ['admin', 'anonymous'], GRANTS);
    expect(out[0].expected_by_role).toEqual({ admin: 200, anonymous: 200 });
  });
});
