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

import db from '@/db/models';
import PersonalityQuizResultsService from '@/services/personality_quiz_results';
import SafetyQuizResultsService from '@/services/safety_quiz_results';
import { FEATURE_PERMISSIONS } from '@/shared/constants/product-permissions';
import { ROLE_NAMES, ROLE_SCOPES } from '@/shared/constants/roles';
import { createTestUser } from '@/test-utils';

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

function parentUserDrilledIntoSchool() {
  return createTestUser({
    id: 'user-1',
    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.READ_PERSONALITY_REPORTS),
        permission(FEATURE_PERMISSIONS.READ_SAFETY_QUIZ_REPORTS),
      ],
    },
    activeScope: {
      level: 'school',
      organizationId: 'org-1',
      schoolId: 'school-1',
      campusId: null,
      classId: null,
    },
  });
}

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

describe('personal result persistence while drilled into child scope', () => {
  test('does not create safety quiz results for parent users in child scope', async () => {
    let createCount = 0;
    mock.method(db.safety_quiz_results, 'create', (async () => {
      createCount += 1;
      return null;
    }) as unknown as typeof db.safety_quiz_results.create);

    const result = await SafetyQuizResultsService.create(
      {
        quiz_id: 'qbs-weekly',
        quiz_title: 'QBS Weekly',
        week_of: '2026-06-15',
        score: 3,
        total_questions: 4,
        answers: [0, 1, 2, 3],
      },
      parentUserDrilledIntoSchool(),
    );

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

  test('normalizes safety quiz result weeks to the Sunday week start on create', async () => {
    const commits: string[] = [];
    const createdRows: 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.safety_quiz_results, 'create', (async (data: unknown) => {
      createdRows.push(data);
      return {
        get: () => ({
          id: 'result-1',
          quiz_id: 'qbs-weekly',
          quiz_title: 'QBS Weekly',
          week_of: '2026-06-14',
          score: 3,
          total_questions: 4,
          answers: [0, 1, 2, 3],
          user_name: 'Emily Johnson',
          user_role: ROLE_NAMES.TEACHER,
          completed_at: new Date('2026-06-17T12:00:00Z'),
          organizationId: 'org-1',
          campusId: 'campus-1',
          userId: 'user-1',
          createdAt: new Date('2026-06-17T12:00:00Z'),
          updatedAt: new Date('2026-06-17T12:00:00Z'),
        }),
      };
    }) as unknown as typeof db.safety_quiz_results.create);

    const result = await SafetyQuizResultsService.create(
      {
        quiz_id: 'qbs-weekly',
        quiz_title: 'QBS Weekly',
        week_of: '2026-06-17',
        score: 3,
        total_questions: 4,
        answers: [0, 1, 2, 3],
      },
      createTestUser({
        id: 'user-1',
        firstName: 'Emily',
        lastName: 'Johnson',
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
        campusId: 'campus-1',
        app_role: {
          name: ROLE_NAMES.TEACHER,
          scope: ROLE_SCOPES.CAMPUS,
          globalAccess: false,
          permissions: [],
        },
      }),
    );

    assert.equal(result?.week_of, '2026-06-14');
    assert.deepEqual(createdRows, [{
      quiz_id: 'qbs-weekly',
      quiz_title: 'QBS Weekly',
      week_of: '2026-06-14',
      score: 3,
      total_questions: 4,
      answers: [0, 1, 2, 3],
      user_name: 'Emily Johnson',
      user_role: ROLE_NAMES.TEACHER,
      completed_at: createdRows.length > 0
        ? (createdRows[0] as { completed_at: Date }).completed_at
        : undefined,
      organizationId: 'org-1',
      campusId: 'campus-1',
      userId: 'user-1',
      createdById: 'user-1',
      updatedById: 'user-1',
    }]);
    assert.deepEqual(commits, ['commit']);
  });

  test('does not upsert personality results for parent users in child scope', async () => {
    let createCount = 0;
    mock.method(db.personality_quiz_results, 'findOne', (async () => null) as unknown as typeof db.personality_quiz_results.findOne);
    mock.method(db.personality_quiz_results, 'create', (async () => {
      createCount += 1;
      return null;
    }) as unknown as typeof db.personality_quiz_results.create);

    const result = await PersonalityQuizResultsService.upsertCurrentUserResult(
      {
        quiz_kind: 'personality_type',
        quiz_id: 'personality-type',
        quiz_title: 'Personality Type Quiz',
        personality_type: 'INFJ',
        quiz_answers: { 1: 'A' },
        total_questions: 1,
      },
      parentUserDrilledIntoSchool(),
    );

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

  test('reads only the current weekly EI self-assessment result for the current user', async () => {
    let capturedWhere: Record<string | symbol, unknown> | null = null;
    mock.method(db.personality_quiz_results, 'findOne', (async (options: { where: Record<string | symbol, unknown> }) => {
      capturedWhere = options.where;
      return null;
    }) as unknown as typeof db.personality_quiz_results.findOne);

    await PersonalityQuizResultsService.getCurrentUserResult(
      { quiz_kind: 'ei_self_assessment' },
      createTestUser({
        id: 'user-1',
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
      }),
    );

    assert.ok(capturedWhere);
    const where = capturedWhere as Record<string | symbol, unknown>;
    assert.equal(where.organizationId, 'org-1');
    assert.equal(where.userId, 'user-1');
    assert.equal(where.quiz_kind, 'ei_self_assessment');
    assert.match(String(where.week_of), /^\d{4}-\d{2}-\d{2}$/);
  });

  test('reads current user EI and personality result history from persisted results', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    let capturedLimit: number | null = null;
    mock.method(db.personality_quiz_results, 'findAll', (async (options: {
      where: Record<string, unknown>;
      limit: number;
    }) => {
      capturedWhere = options.where;
      capturedLimit = options.limit;
      return [
        {
          get: () => ({
            id: 'personality-1',
            quiz_kind: 'personality_type',
            quiz_id: 'personality-type',
            quiz_title: 'Personality Type Quiz',
            week_of: null,
            personality_type: 'INFJ',
            quiz_answers: { 1: 'I' },
            score: null,
            total_questions: 1,
            result_label: 'INFJ',
            result_payload: null,
            user_name: 'Emily Johnson',
            user_role: ROLE_NAMES.TEACHER,
            completed_at: new Date('2026-06-17T12:00:00Z'),
            organizationId: 'org-1',
            campusId: 'campus-1',
            userId: 'user-1',
            createdAt: new Date('2026-06-17T12:00:00Z'),
            updatedAt: new Date('2026-06-17T12:00:00Z'),
          }),
        },
      ];
    }) as unknown as typeof db.personality_quiz_results.findAll);

    const result = await PersonalityQuizResultsService.getCurrentUserHistory(
      {},
      createTestUser({
        id: 'user-1',
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
      }),
    );

    assert.deepEqual(capturedWhere, {
      organizationId: 'org-1',
      userId: 'user-1',
    });
    assert.equal(capturedLimit, 25);
    assert.equal(result.count, 1);
    assert.equal(result.rows[0]?.quiz_kind, 'personality_type');
    assert.equal(result.rows[0]?.personality_type, 'INFJ');
  });

  test('reads the current user safety quiz status from saved results', async () => {
    let capturedWhere: Record<string, unknown> | null = null;
    mock.method(db.safety_quiz_results, 'findOne', (async (options: { where: Record<string, unknown> }) => {
      capturedWhere = options.where;
      return {
      get: () => ({
        id: 'result-1',
        quiz_id: 'qbs-weekly',
          quiz_title: 'QBS Weekly',
          week_of: '2026-06-14',
          score: 4,
          total_questions: 4,
          answers: [0, 1, 2, 3],
          user_name: 'Emily Johnson',
          user_role: ROLE_NAMES.TEACHER,
          completed_at: new Date('2026-06-17T12:00:00Z'),
          organizationId: 'org-1',
          campusId: 'campus-1',
          userId: 'user-1',
          createdAt: new Date('2026-06-17T12:00:00Z'),
          updatedAt: new Date('2026-06-17T12:00:00Z'),
        }),
      };
    }) as unknown as typeof db.safety_quiz_results.findOne);

    const result = await SafetyQuizResultsService.me(
      { week_of: '2026-06-17' },
      createTestUser({
        id: 'user-1',
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
      }),
    );

    assert.equal(result.completed, true);
    assert.equal(result.result?.id, 'result-1');
    assert.deepEqual(capturedWhere, {
      userId: 'user-1',
      week_of: '2026-06-14',
    });
  });

  test('builds safety quiz completion rows for completed and pending staff', async () => {
    mock.method(db.users, 'findAll', (async () => [
      {
        id: 'user-1',
        firstName: 'Emily',
        lastName: 'Johnson',
        email: 'teacher@flatlogic.com',
        app_role: { name: ROLE_NAMES.TEACHER },
      },
      {
        id: 'user-2',
        firstName: 'Marcus',
        lastName: 'Davis',
        email: 'support@flatlogic.com',
        app_role: { name: ROLE_NAMES.SUPPORT_STAFF },
      },
    ]) as unknown as typeof db.users.findAll);
    mock.method(db.safety_quiz_results, 'findAll', (async () => [
      {
        userId: 'user-1',
        get: () => ({
          id: 'result-1',
          quiz_id: 'qbs-weekly',
          quiz_title: 'QBS Weekly',
          week_of: '2026-06-15',
          score: 4,
          total_questions: 4,
          answers: [0, 1, 2, 3],
          user_name: 'Emily Johnson',
          user_role: ROLE_NAMES.TEACHER,
          completed_at: new Date('2026-06-17T12:00:00Z'),
          organizationId: 'org-1',
          campusId: 'campus-1',
          userId: 'user-1',
          createdAt: new Date('2026-06-17T12:00:00Z'),
          updatedAt: new Date('2026-06-17T12:00:00Z'),
        }),
      },
    ]) as unknown as typeof db.safety_quiz_results.findAll);

    const report = await SafetyQuizResultsService.completion(
      { week_of: '2026-06-15' },
      createTestUser({
        id: 'director-1',
        organizationId: 'org-1',
        organizations: { id: 'org-1' },
        campusId: 'campus-1',
        app_role: {
          name: ROLE_NAMES.DIRECTOR,
          scope: ROLE_SCOPES.CAMPUS,
          globalAccess: false,
          permissions: [permission(FEATURE_PERMISSIONS.READ_SAFETY_QUIZ_REPORTS)],
        },
      }),
    );

    assert.deepEqual(report.summary, {
      totalStaff: 2,
      completedCount: 1,
      pendingCount: 1,
      completionRate: 50,
    });
    assert.equal(report.rows[0]?.status, 'complete');
    assert.equal(report.rows[1]?.status, 'pending');
  });

  test('builds EI completion from current-week self-assessment and all-time personality results', async () => {
    mock.method(db.users, 'findAll', (async () => [
      {
        id: 'user-1',
        firstName: 'Emily',
        lastName: 'Johnson',
        email: 'teacher@flatlogic.com',
        app_role: { name: ROLE_NAMES.TEACHER },
      },
    ]) as unknown as typeof db.users.findAll);

    let capturedWhere: Record<string | symbol, unknown> | null = null;
    mock.method(db.personality_quiz_results, 'findAll', (async (options: { where: Record<string | symbol, unknown> }) => {
      capturedWhere = options.where;
      return [];
    }) as unknown as typeof db.personality_quiz_results.findAll);

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

    assert.ok(capturedWhere);
    const where = capturedWhere as Record<string | symbol, unknown>;
    const kindFilter = where[Op.or] as Array<Record<string, unknown>> | undefined;
    assert.equal(where.organizationId, 'org-1');
    assert.deepEqual(where.userId, { [Op.in]: ['user-1'] });
    assert.equal(kindFilter?.[0]?.quiz_kind, 'personality_type');
    assert.equal(kindFilter?.[1]?.quiz_kind, 'ei_self_assessment');
    assert.match(String(kindFilter?.[1]?.week_of), /^\d{4}-\d{2}-\d{2}$/);
    assert.deepEqual(report.summary, {
      totalStaff: 1,
      completedCount: 0,
      pendingCount: 1,
      selfAssessmentCompletedCount: 0,
      personalityCompletedCount: 0,
      completionRate: 0,
    });
  });
});
