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

import db from '@/db/models';
import ZoneCheckinService from '@/services/zone-checkin';
import UserProgressService from '@/services/user_progress';
import ForbiddenError from '@/shared/errors/forbidden';
import { FEATURE_PERMISSIONS } from '@/shared/constants/product-permissions';
import { ROLE_NAMES } from '@/shared/constants/roles';
import { localDateInTimezone } from '@/shared/constants/timezone';
import { createGlobalAccessUser, createTestUser } from '@/test-utils';
import type { CurrentUser } from '@/db/api/types';

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

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

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

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

  test('allows an explicit ZONE_CHECKIN permission regardless of role name', async () => {
    mock.method(db.campuses, 'findByPk', (async () => ({
      timezone: 'America/Phoenix',
    })) as typeof db.campuses.findByPk);
    mock.method(UserProgressService, 'list', (async () => ({
      rows: [],
      count: 0,
    })) as typeof UserProgressService.list);

    const result = await ZoneCheckinService.today(user(ROLE_NAMES.OWNER));

    assert.equal(result.zone, null);
    assert.equal(result.isCheckedInToday, false);
  });

  test('allows a campus workflow role to read today status', async () => {
    mock.method(db.campuses, 'findByPk', (async () => ({
      timezone: 'America/Phoenix',
    })) as typeof db.campuses.findByPk);
    mock.method(UserProgressService, 'list', (async () => ({
      rows: [],
      count: 0,
    })) as typeof UserProgressService.list);

    const result = await ZoneCheckinService.today(user(ROLE_NAMES.TEACHER));

    assert.equal(result.zone, null);
    assert.equal(result.isCheckedInToday, false);
    assert.match(result.date, /^\d{4}-\d{2}-\d{2}$/);
  });

  test('blocks parent-drill zone mutations', async () => {
    const currentUser = createTestUser({
      organizationId: 'org-1',
      organizations: { id: 'org-1' },
      campusId: null,
      app_role: {
        name: ROLE_NAMES.OWNER,
        scope: 'organization',
        globalAccess: false,
        permissions: [{ name: FEATURE_PERMISSIONS.ZONE_CHECKIN }],
      },
      activeScope: {
        level: 'campus',
        organizationId: 'org-1',
        schoolId: 'school-1',
        campusId: 'campus-1',
        classId: null,
      },
    });

    await assert.rejects(
      () => ZoneCheckinService.checkIn({ zone: 'green' }, currentUser),
      ForbiddenError,
    );
    await assert.rejects(
      () => ZoneCheckinService.clearToday(currentUser),
      ForbiddenError,
    );
  });
});

describe('ZoneCheckinService completion report', () => {
  test('builds scoped daily rows and counts non-green zones', async () => {
    const currentUser = createTestUser({
      organizationId: 'org-1',
      app_role: {
        name: ROLE_NAMES.DIRECTOR,
        scope: 'campus',
        globalAccess: false,
        permissions: [{ name: FEATURE_PERMISSIONS.READ_ZONE_CHECKIN_REPORTS }],
      },
      campusId: 'campus-1',
    });
    mock.method(db.users, 'findAll', (async () => [
      {
        id: 'user-1',
        firstName: 'Ava',
        lastName: 'Lee',
        email: 'ava@example.test',
        campusId: 'campus-1',
        app_role: { name: ROLE_NAMES.TEACHER },
      },
      {
        id: 'user-2',
        firstName: 'Ben',
        lastName: 'Stone',
        email: 'ben@example.test',
        campusId: 'campus-1',
        app_role: { name: ROLE_NAMES.SUPPORT_STAFF },
      },
    ]) as typeof db.users.findAll);
    mock.method(db.campuses, 'findAll', (async () => [
      { id: 'campus-1', timezone: 'UTC' },
    ]) as typeof db.campuses.findAll);
    mock.method(db.user_progress, 'findAll', (async () => [
      {
        userId: 'user-1',
        item_id: localDateInTimezone('UTC'),
        value: 'yellow',
      },
    ]) as typeof db.user_progress.findAll);

    const report = await ZoneCheckinService.completion(currentUser);

    assert.equal(report.summary.totalStaff, 2);
    assert.equal(report.summary.completedCount, 1);
    assert.equal(report.summary.pendingCount, 1);
    assert.equal(report.summary.nonGreenCount, 1);
    assert.equal(report.rows[0].result, 'Yellow Zone');
    assert.equal(report.rows[0].riskLevel, 'medium');
    assert.equal(report.rows[1].status, 'pending');
  });
});
