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

import db from '@/db/models';
import ContentCatalogService from '@/services/content_catalog';
import { createGlobalAccessUser } from '@/test-utils';
import { FEATURE_PERMISSIONS } from '@/shared/constants/product-permissions';
import { ROLE_NAMES, ROLE_SCOPES } from '@/shared/constants/roles';

function catalogRecord(payload: unknown) {
  return {
    get: () => ({
      id: 'catalog-1',
      content_type: 'dashboard-encouraging-quotes',
      payload,
      updatedAt: new Date('2026-06-16T00:00:00Z'),
    }),
  };
}

function organizationContentManager() {
  return createGlobalAccessUser({
    app_role: {
      name: ROLE_NAMES.SUPER_ADMIN,
      scope: ROLE_SCOPES.SYSTEM,
      globalAccess: true,
      permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_CONTENT_CATALOG }],
    },
    activeScope: {
      level: ROLE_SCOPES.ORGANIZATION,
      organizationId: 'org-1',
      schoolId: null,
      campusId: null,
      classId: null,
    },
  });
}

function campusEsaManager() {
  return createGlobalAccessUser({
    app_role: {
      name: ROLE_NAMES.DIRECTOR,
      scope: ROLE_SCOPES.CAMPUS,
      globalAccess: false,
      permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_ESA_FUNDING_CONTENT }],
    },
    organizationId: 'org-1',
    campusId: 'campus-1',
  });
}

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

describe('ContentCatalogService tenant scoping', () => {
  test('reads dashboard content at an organization drill-down scope', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return catalogRecord([]);
    }) as typeof db.content_catalog.findOne);

    await ContentCatalogService.findByType(
      'dashboard-encouraging-quotes',
      createGlobalAccessUser({
        activeScope: {
          level: 'organization',
          organizationId: 'org-1',
          schoolId: null,
          campusId: null,
          classId: null,
        },
      }),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'dashboard-encouraging-quotes',
      active: true,
      organizationId: 'org-1',
      schoolId: null,
      campusId: null,
      classId: null,
    });
  });

  test('reads dashboard content at a school drill-down scope', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return catalogRecord([]);
    }) as typeof db.content_catalog.findOne);

    await ContentCatalogService.findByType(
      'dashboard-encouraging-quotes',
      createGlobalAccessUser({
        activeScope: {
          level: 'school',
          organizationId: 'org-1',
          schoolId: 'school-1',
          campusId: null,
          classId: null,
        },
      }),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'dashboard-encouraging-quotes',
      active: true,
      organizationId: 'org-1',
      schoolId: 'school-1',
      campusId: null,
      classId: null,
    });
  });

  test('reads dashboard content at a campus drill-down scope', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return catalogRecord([]);
    }) as typeof db.content_catalog.findOne);

    await ContentCatalogService.findByType(
      'dashboard-encouraging-quotes',
      createGlobalAccessUser({
        activeScope: {
          level: 'campus',
          organizationId: 'org-1',
          schoolId: 'school-1',
          campusId: 'campus-1',
          classId: null,
        },
      }),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'dashboard-encouraging-quotes',
      active: true,
      organizationId: 'org-1',
      campusId: 'campus-1',
      classId: null,
    });
  });

  test('allows classroom strategy management from organization scope', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return catalogRecord([]);
    }) as typeof db.content_catalog.findOne);

    await ContentCatalogService.findManagedByType(
      'classroom-strategies',
      organizationContentManager(),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'classroom-strategies',
      active: true,
      organizationId: 'org-1',
    });
  });

  test('rejects classroom strategy management outside organization scope', async () => {
    await assert.rejects(
      () => ContentCatalogService.findManagedByType(
        'classroom-strategies',
        createGlobalAccessUser({
          app_role: {
            name: ROLE_NAMES.SUPER_ADMIN,
            scope: ROLE_SCOPES.SYSTEM,
            globalAccess: true,
            permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_CONTENT_CATALOG }],
          },
          activeScope: {
            level: ROLE_SCOPES.CAMPUS,
            organizationId: 'org-1',
            schoolId: 'school-1',
            campusId: 'campus-1',
            classId: null,
          },
        }),
      ),
      { name: 'ForbiddenError' },
    );
  });

  test('rejects sign library management outside organization scope', async () => {
    await assert.rejects(
      () => ContentCatalogService.findManagedByType(
        'sign-language-items',
        createGlobalAccessUser({
          app_role: {
            name: ROLE_NAMES.SUPER_ADMIN,
            scope: ROLE_SCOPES.SYSTEM,
            globalAccess: true,
            permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_CONTENT_CATALOG }],
          },
          activeScope: {
            level: ROLE_SCOPES.CAMPUS,
            organizationId: 'org-1',
            schoolId: 'school-1',
            campusId: 'campus-1',
            classId: null,
          },
        }),
      ),
      { name: 'ForbiddenError' },
    );
  });

  test('rejects QBS quiz management outside organization scope', async () => {
    await assert.rejects(
      () => ContentCatalogService.findManagedByType(
        'safety-qbs-quiz',
        createGlobalAccessUser({
          app_role: {
            name: ROLE_NAMES.SUPER_ADMIN,
            scope: ROLE_SCOPES.SYSTEM,
            globalAccess: true,
            permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_CONTENT_CATALOG }],
          },
          activeScope: {
            level: ROLE_SCOPES.CAMPUS,
            organizationId: 'org-1',
            schoolId: 'school-1',
            campusId: 'campus-1',
            classId: null,
          },
        }),
      ),
      { name: 'ForbiddenError' },
    );
  });

  test('rejects Emotional Intelligence quiz management outside organization scope', async () => {
    await assert.rejects(
      () => ContentCatalogService.findManagedByType(
        'emotional-intelligence-assessment-questions',
        createGlobalAccessUser({
          app_role: {
            name: ROLE_NAMES.SUPER_ADMIN,
            scope: ROLE_SCOPES.SYSTEM,
            globalAccess: true,
            permissions: [{ name: FEATURE_PERMISSIONS.MANAGE_CONTENT_CATALOG }],
          },
          activeScope: {
            level: ROLE_SCOPES.CAMPUS,
            organizationId: 'org-1',
            schoolId: 'school-1',
            campusId: 'campus-1',
            classId: null,
          },
        }),
      ),
      { name: 'ForbiddenError' },
    );
  });

  test('versions QBS quiz content updates so prior weekly quiz payloads remain stored', async () => {
    const commits: string[] = [];
    const updatedRows: unknown[] = [];
    const createdRows: unknown[] = [];
    let capturedWhere: Record<string, unknown> | null = null;

    mock.method(db.sequelize, 'transaction', (async () => ({
      commit: async () => { commits.push('commit'); },
      rollback: async () => { commits.push('rollback'); },
    })) as typeof db.sequelize.transaction);

    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return {
        update: async (data: unknown) => {
          updatedRows.push(data);
          return null;
        },
        get: () => ({
          id: 'catalog-old',
          content_type: 'safety-qbs-quiz',
          payload: { title: 'Old Quiz' },
          updatedAt: new Date('2026-06-14T00:00:00Z'),
        }),
      };
    }) as unknown as typeof db.content_catalog.findOne);

    mock.method(db.content_catalog, 'create', (async (data: unknown) => {
      createdRows.push(data);
      return {
        get: () => ({
          id: 'catalog-new',
          content_type: 'safety-qbs-quiz',
          payload: { title: 'New Quiz' },
          updatedAt: new Date('2026-06-18T00:00:00Z'),
        }),
      };
    }) as unknown as typeof db.content_catalog.create);

    const result = await ContentCatalogService.update(
      'safety-qbs-quiz',
      { payload: { title: 'New Quiz' } },
      organizationContentManager(),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'safety-qbs-quiz',
      active: true,
      organizationId: 'org-1',
    });
    assert.deepEqual(updatedRows, [{ active: false }]);
    assert.deepEqual(createdRows, [{
      content_type: 'safety-qbs-quiz',
      payload: { title: 'New Quiz' },
      active: true,
      importHash: null,
      organizationId: 'org-1',
      schoolId: null,
      campusId: null,
      classId: null,
    }]);
    assert.equal(result.id, 'catalog-new');
    assert.deepEqual(commits, ['commit']);
  });

  test('versions Emotional Intelligence quiz content updates so old quiz payloads remain stored', async () => {
    const commits: string[] = [];
    const updatedRows: unknown[] = [];
    const createdRows: unknown[] = [];
    let capturedWhere: Record<string, unknown> | null = null;

    mock.method(db.sequelize, 'transaction', (async () => ({
      commit: async () => { commits.push('commit'); },
      rollback: async () => { commits.push('rollback'); },
    })) as typeof db.sequelize.transaction);

    mock.method(db.content_catalog, 'findOne', (async (options: { where?: Record<string, unknown> }) => {
      capturedWhere = options.where ?? null;
      return {
        update: async (data: unknown) => {
          updatedRows.push(data);
          return null;
        },
        get: () => ({
          id: 'catalog-old',
          content_type: 'emotional-intelligence-personality-quiz',
          payload: { title: 'Old Personality Quiz' },
          updatedAt: new Date('2026-06-14T00:00:00Z'),
        }),
      };
    }) as unknown as typeof db.content_catalog.findOne);

    mock.method(db.content_catalog, 'create', (async (data: unknown) => {
      createdRows.push(data);
      return {
        get: () => ({
          id: 'catalog-new',
          content_type: 'emotional-intelligence-personality-quiz',
          payload: { title: 'New Personality Quiz' },
          updatedAt: new Date('2026-06-18T00:00:00Z'),
        }),
      };
    }) as unknown as typeof db.content_catalog.create);

    const result = await ContentCatalogService.update(
      'emotional-intelligence-personality-quiz',
      { payload: { title: 'New Personality Quiz' } },
      organizationContentManager(),
    );

    assert.deepEqual(capturedWhere, {
      content_type: 'emotional-intelligence-personality-quiz',
      active: true,
      organizationId: 'org-1',
    });
    assert.deepEqual(updatedRows, [{ active: false }]);
    assert.deepEqual(createdRows, [{
      content_type: 'emotional-intelligence-personality-quiz',
      payload: { title: 'New Personality Quiz' },
      active: true,
      importHash: null,
      organizationId: 'org-1',
      schoolId: null,
      campusId: null,
      classId: null,
    }]);
    assert.equal(result.id, 'catalog-new');
    assert.deepEqual(commits, ['commit']);
  });

  test('ESA funding updates bump acknowledgment policy version with section summary', async () => {
    const commits: string[] = [];
    const catalogUpdates: unknown[] = [];
    const policyUpdates: unknown[] = [];

    mock.method(db.sequelize, 'transaction', (async () => ({
      commit: async () => { commits.push('commit'); },
      rollback: async () => { commits.push('rollback'); },
    })) as typeof db.sequelize.transaction);

    mock.method(db.content_catalog, 'findOne', (async () => ({
      update: async (data: unknown) => {
        catalogUpdates.push(data);
        return {
          get: () => ({
            id: 'esa-content-1',
            content_type: 'esa-funding-content',
            payload: { resources: [] },
            updatedAt: new Date('2026-06-22T00:00:00Z'),
          }),
        };
      },
      get: () => ({
        id: 'esa-content-1',
        content_type: 'esa-funding-content',
        payload: { resources: [] },
        updatedAt: new Date('2026-06-22T00:00:00Z'),
      }),
    })) as unknown as typeof db.content_catalog.findOne);

    mock.method(db.policy_documents, 'findOne', (async () => ({
      version: 4,
      update: async (data: unknown) => {
        policyUpdates.push(data);
        return null;
      },
    })) as unknown as typeof db.policy_documents.findOne);

    await ContentCatalogService.update(
      'esa-funding-content',
      {
        payload: { resources: [] },
        changeSummary: 'Helpful Resources',
      },
      campusEsaManager(),
    );

    assert.deepEqual(catalogUpdates, [{ payload: { resources: [] }, active: true }]);
    assert.deepEqual(policyUpdates, [{
      body: 'Staff must review the current ESA Funding Information page and acknowledge understanding of the campus guidance.\n\nUpdated section: Helpful Resources',
      version: 5,
    }]);
    assert.deepEqual(commits, ['commit']);
  });
});
