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

import db from '@/db/models';
import UserProgressService from '@/services/user_progress';
import { USER_PROGRESS_TYPES } from '@/shared/constants/user-progress';
import { ROLE_NAMES, ROLE_SCOPES } from '@/shared/constants/roles';
import { createTestUser } from '@/test-utils';
import type { CurrentUser } from '@/db/api/types';

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

function parentUserInChildScope(): CurrentUser {
  return 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: [],
    },
    activeScope: {
      level: 'school',
      organizationId: 'org-1',
      schoolId: 'school-1',
      campusId: null,
      classId: null,
    },
  });
}

describe('UserProgressService drilled scope persistence guard', () => {
  test('upserts classroom strategy favorites as user progress', async () => {
    const actor = createTestUser({
      id: 'user-1',
      organizationId: 'org-1',
      organizations: { id: 'org-1' },
      campusId: 'campus-1',
      app_role: {
        name: ROLE_NAMES.DIRECTOR,
        scope: ROLE_SCOPES.CAMPUS,
        globalAccess: false,
        permissions: [],
      },
    });
    let createdPayload: Record<string, unknown> | undefined;

    mock.method(db.sequelize, 'transaction', async () => ({
      commit: async () => undefined,
      rollback: async () => undefined,
    }));
    mock.method(db.user_progress, 'findOne', async () => null);
    mock.method(db.user_progress, 'create', async (payload: Record<string, unknown>) => {
      createdPayload = payload;
      return {
        get: () => ({
          id: 'progress-1',
          ...payload,
          createdAt: new Date('2026-06-17T12:00:00Z'),
          updatedAt: new Date('2026-06-17T12:00:00Z'),
        }),
      };
    });

    const result = await UserProgressService.upsert(
      {
        progress_type: USER_PROGRESS_TYPES.CLASSROOM_STRATEGY_FAVORITE,
        item_id: 'token-economy',
        value: 'Token Economy System',
      },
      actor,
    );

    assert.ok(createdPayload);
    assert.equal(createdPayload.progress_type, USER_PROGRESS_TYPES.CLASSROOM_STRATEGY_FAVORITE);
    assert.equal(createdPayload.item_id, 'token-economy');
    assert.equal(createdPayload.value, 'Token Economy System');
    assert.equal(createdPayload.organizationId, 'org-1');
    assert.equal(createdPayload.campusId, 'campus-1');
    assert.equal(createdPayload.userId, 'user-1');
    assert.equal(result?.item_id, 'token-economy');
  });

  test('does not list own progress while a parent user is drilled into a child scope', async () => {
    let listCount = 0;
    mock.method(db.user_progress, 'findAndCountAll', (async () => {
      listCount += 1;
      return { rows: [], count: 0 };
    }) as unknown as typeof db.user_progress.findAndCountAll);

    const result = await UserProgressService.list(
      { progress_type: USER_PROGRESS_TYPES.SIGN_LEARNED },
      parentUserInChildScope(),
    );

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

  test('does not upsert progress while a parent user is drilled into a child scope', async () => {
    let createCount = 0;
    mock.method(db.user_progress, 'create', (async () => {
      createCount += 1;
      return null;
    }) as unknown as typeof db.user_progress.create);

    const result = await UserProgressService.upsert(
      {
        progress_type: USER_PROGRESS_TYPES.SIGN_LEARNED,
        item_id: 'help',
        value: 'Help',
      },
      parentUserInChildScope(),
    );

    assert.equal(result, null);
    assert.equal(createCount, 0);
  });

  test('does not delete progress while a parent user is drilled into a child scope', async () => {
    let deleteCount = 0;
    mock.method(db.user_progress, 'destroy', (async () => {
      deleteCount += 1;
      return 1;
    }) as unknown as typeof db.user_progress.destroy);

    const result = await UserProgressService.removeByItem(
      { progress_type: USER_PROGRESS_TYPES.SIGN_LEARNED, item_id: 'help' },
      parentUserInChildScope(),
    );

    assert.deepEqual(result, { deletedCount: 0 });
    assert.equal(deleteCount, 0);
  });
});
