// Integration test: walks the SCREENS.md role-permission matrix rows that
// have a clear View/CRUD answer and asserts the matching HTTP status code
// for every role × every representative route.
//
// These specs do NOT touch the real database. They mount a tiny Express
// app with the requireRole middleware in front of placeholder handlers,
// then poke each (role, route) cell of the matrix.
//
// The cells below are lifted directly from .harness/SCREENS.md §"Role
// Permissions Matrix" (and stable). When that table changes, update the
// `MATRIX` array below; this file is the executable form of the doc.

const { ROLE, requireRole } = require('../src/middleware/requireRole');
const express = require('express');
const request = require('supertest');

function buildMatrixApp() {
  const app = express();
  app.use(express.json());
  // Pretend-auth: substitute req.user from the X-Test-Role header so we
  // exercise the actual requireRole middleware with realistic shapes.
  app.use((req, res, next) => {
    const role = req.get('X-Test-Role');
    req.user = role ? { id: 1, role } : null;
    next();
  });

  // Each entry mirrors a cell of SCREENS.md. The allowed list reflects
  // the "View" or "CRUD" cell for that row. Use 'execute' for /exams/take
  // — that route is student-only and is closed to all other roles.
  const routes = [
    { method: 'get', path: '/api/settings',          allowed: [ROLE.SYSTEMS_ADMIN] },
    { method: 'get', path: '/api/audit-logs',        allowed: [ROLE.SYSTEMS_ADMIN] },
    { method: 'get', path: '/api/users',             allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL] },
    { method: 'get', path: '/api/departments',       allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL] },
    { method: 'get', path: '/api/hr/management',     allowed: [ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.HR] },
    { method: 'get', path: '/api/finance/payroll',   allowed: [ROLE.BURSAR] },
    { method: 'get', path: '/api/fees',              allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.BURSAR, ROLE.STUDENT] },
    { method: 'get', path: '/api/front-office/visitors', allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.TEACHER] },
    { method: 'get', path: '/api/attendance/take',   allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.TEACHER] },
    { method: 'get', path: '/api/my-courses',        allowed: [ROLE.TEACHER, ROLE.STUDENT] },
    { method: 'get', path: '/api/exams/take',        allowed: [ROLE.STUDENT] },
    { method: 'get', path: '/api/clubs-management',  allowed: [ROLE.CLUBS_HEAD] },
    { method: 'get', path: '/api/notice-board',      allowed: [
      ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.HR,
      ROLE.TEACHER, ROLE.STUDENT, ROLE.PARENT, ROLE.CLUBS_HEAD,
    ] },
    { method: 'get', path: '/api/extracurriculars',  allowed: [
      ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.HR,
      ROLE.TEACHER, ROLE.STUDENT, ROLE.PARENT, ROLE.CLUBS_HEAD,
    ] },
  ];
  for (const r of routes) {
    app[r.method](r.path, requireRole(...r.allowed), (req, res) => res.json({ ok: true }));
  }

  return app;
}

const MATRIX_ROWS = [
  { path: '/api/settings',            allowed: [ROLE.SYSTEMS_ADMIN] },
  { path: '/api/audit-logs',          allowed: [ROLE.SYSTEMS_ADMIN] },
  { path: '/api/users',               allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL] },
  { path: '/api/departments',         allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL] },
  { path: '/api/hr/management',       allowed: [ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.HR] },
  { path: '/api/finance/payroll',     allowed: [ROLE.BURSAR] },
  { path: '/api/fees',                allowed: [ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.BURSAR, ROLE.STUDENT] },
  { path: '/api/notice-board',        allowed: [
    ROLE.SYSTEMS_ADMIN, ROLE.SCHOOL_ADMIN, ROLE.PRINCIPAL, ROLE.HR,
    ROLE.TEACHER, ROLE.STUDENT, ROLE.PARENT, ROLE.CLUBS_HEAD,
  ] },
  { path: '/api/exams/take',          allowed: [ROLE.STUDENT] },
  { path: '/api/clubs-management',    allowed: [ROLE.CLUBS_HEAD] },
];

const ALL_ROLES = Object.values(ROLE).concat([null]);

describe('SCREENS.md role-permission matrix', () => {
  const app = buildMatrixApp();
  for (const { path, allowed } of MATRIX_ROWS) {
    for (const role of ALL_ROLES) {
      const expected = role === null ? 401 : (allowed.includes(role) ? 200 : 403);
      const label = role === null ? 'no-token' : role;
      it(`${label.padEnd(15)} -> ${expected} ${path}`, async () => {
        const req = request(app).get(path);
        if (role) req.set('X-Test-Role', role);
        const res = await req;
        expect(res.status).toBe(expected);
      });
    }
  }
});

describe('SCREENS.md negative cells (rows where the role is forbidden)', () => {
  // A focused subset: every role that is *not* in the allowed list for a
  // sensitive route must get 403. Useful as a regression net against
  // accidentally widening access.
  const app = buildMatrixApp();
  const strictRows = [
    { path: '/api/audit-logs',        expectedRoles: [ROLE.STUDENT, ROLE.PARENT, ROLE.TEACHER, ROLE.PRINCIPAL] },
    { path: '/api/clubs-management',  expectedRoles: [ROLE.STUDENT, ROLE.PARENT, ROLE.TEACHER, ROLE.PRINCIPAL] },
    { path: '/api/finance/payroll',   expectedRoles: [ROLE.STUDENT, ROLE.PARENT, ROLE.TEACHER, ROLE.PRINCIPAL] },
  ];
  for (const { path, expectedRoles } of strictRows) {
    for (const role of expectedRoles) {
      it(`forbids ${role} from ${path}`, async () => {
        const res = await request(app).get(path).set('X-Test-Role', role);
        expect(res.status).toBe(403);
      });
    }
  }
});
