import { afterEach, describe, mock, test } from 'node:test';
import assert from 'node:assert/strict';

import db from '@/db/models';
import PolicyAcknowledgmentsService from '@/services/policy_acknowledgments';
import ForbiddenError from '@/shared/errors/forbidden';
import { FEATURE_PERMISSIONS } from '@/shared/constants/product-permissions';
import { ROLE_NAMES, ROLE_SCOPES } from '@/shared/constants/roles';
import { createGlobalAccessUser, createTestUser } from '@/test-utils';
import type { CurrentUser } from '@/db/api/types';

function permission(name: string) {
  return { name };
}

function user(roleName: string, permissions: readonly string[] = []): CurrentUser {
  return createTestUser({
    campusId: 'campus-1',
    app_role: {
      name: roleName,
      globalAccess: false,
      permissions: permissions.map(permission),
    },
  });
}

afterEach(() => {
  mock.restoreAll();
});

describe('PolicyAcknowledgmentsService role eligibility', () => {
  test('rejects global access without explicit ACK_POLICY', async () => {
    const rejectedUsers = [
      createGlobalAccessUser(),
    ];

    for (const currentUser of rejectedUsers) {
      await assert.rejects(
        () => PolicyAcknowledgmentsService.list({}, currentUser),
        ForbiddenError,
      );
    }
  });

  test('allows an explicit ACK_POLICY permission regardless of role name', async () => {
    mock.method(db.policy_acknowledgments, 'findAndCountAll', (async () => ({
      rows: [],
      count: 0,
    })) as unknown as typeof db.policy_acknowledgments.findAndCountAll);

    const result = await PolicyAcknowledgmentsService.list(
      {},
      user(ROLE_NAMES.OWNER, [FEATURE_PERMISSIONS.ACK_POLICY]),
    );

    assert.deepEqual(result, { rows: [], count: 0 });
  });

  test('does not persist acknowledgments while a parent user is drilled into a child scope', async () => {
    let documentLookupCount = 0;
    let acknowledgmentCreateCount = 0;
    mock.method(db.policy_documents, 'findOne', (async () => {
      documentLookupCount += 1;
      return null;
    }) as unknown as typeof db.policy_documents.findOne);
    mock.method(db.policy_acknowledgments, 'create', (async () => {
      acknowledgmentCreateCount += 1;
      return null;
    }) as unknown as typeof db.policy_acknowledgments.create);

    const result = await PolicyAcknowledgmentsService.acknowledge(
      { policyDocumentId: 'document-1' },
      createTestUser({
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
        schoolId: null,
        campusId: null,
        app_role: {
          name: ROLE_NAMES.SUPERINTENDENT,
          scope: ROLE_SCOPES.ORGANIZATION,
          globalAccess: false,
          permissions: [permission(FEATURE_PERMISSIONS.ACK_POLICY)],
        },
        activeScope: {
          level: 'school',
          organizationId: 'org-1',
          schoolId: 'school-1',
          campusId: null,
          classId: null,
        },
      }),
    );

    assert.equal(result, null);
    assert.equal(documentLookupCount, 0);
    assert.equal(acknowledgmentCreateCount, 0);
  });

  test('builds current-version report rows with missing staff in scope', async () => {
    mock.method(db.policy_documents, 'findAll', (async () => [
      {
        id: 'document-1',
        title: 'Fire Drill Procedures',
        category: 'safety_protocol',
        version: 2,
      },
    ]) as unknown as typeof db.policy_documents.findAll);

    mock.method(db.users, 'findAll', (async () => [
      {
        id: 'user-1',
        firstName: 'Ava',
        lastName: 'Lee',
        email: 'ava@example.test',
        campusId: 'campus-1',
        schoolId: 'school-1',
        classId: 'class-1',
        app_role: { name: ROLE_NAMES.TEACHER },
      },
      {
        id: 'user-2',
        firstName: 'Ben',
        lastName: 'Cruz',
        email: 'ben@example.test',
        campusId: 'campus-1',
        schoolId: 'school-1',
        classId: 'class-1',
        app_role: { name: ROLE_NAMES.SUPPORT_STAFF },
      },
    ]) as unknown as typeof db.users.findAll);

    mock.method(db.policy_acknowledgments, 'findAll', (async () => [
      {
        userId: 'user-1',
        policyDocumentId: 'document-1',
        version: 2,
        acknowledgedAt: new Date('2026-06-18T10:00:00.000Z'),
      },
      {
        userId: 'user-2',
        policyDocumentId: 'document-1',
        version: 1,
        acknowledgedAt: new Date('2026-06-17T10:00:00.000Z'),
      },
    ]) as unknown as typeof db.policy_acknowledgments.findAll);

    const report = await PolicyAcknowledgmentsService.report(createTestUser({
      organizationId: 'org-1',
      organizations: { id: 'org-1' },
      schoolId: 'school-1',
      campusId: 'campus-1',
      app_role: {
        name: ROLE_NAMES.DIRECTOR,
        scope: ROLE_SCOPES.CAMPUS,
        globalAccess: false,
        permissions: [permission(FEATURE_PERMISSIONS.READ_POLICY_ACKNOWLEDGMENT_REPORTS)],
      },
    }));

    assert.deepEqual(report.summary, {
      scope: 'campus',
      totalDocuments: 1,
      totalStaff: 2,
      totalRequired: 2,
      acknowledgedCount: 1,
      missingCount: 1,
      completionRate: 50,
    });
    assert.deepEqual(report.documents, [
      {
        id: 'document-1',
        title: 'Fire Drill Procedures',
        category: 'safety_protocol',
        version: 2,
        totalStaff: 2,
        acknowledgedCount: 1,
        missingCount: 1,
        completionRate: 50,
      },
    ]);
    assert.equal(report.staff[1]?.name, 'Ben Cruz');
    assert.equal(report.staff[1]?.documents[0]?.missing, true);
  });
});
