import { ErrorCodes, getAllIntersectionsOfArrays, NotFoundError, UtilsPort } from '@humand-packages/common';
import { faker } from '@faker-js/faker/locale/en_US';
import { mock, MockProxy } from 'jest-mock-extended';
import { SegmentationType } from '../../../src/api/modules/audiences/business/constants/segmentationTypes';
import { SEGMENTABLE_TYPES } from '../../../src/api/modules/audiences/business/models/segmentableEntityType';
import { PerformanceReviewCycle } from '../../../src/api/modules/performanceReviews/business/models/performanceReviewCycle';
import { PerformanceReviewCalibrationAudiencesPort } from '../../../src/api/modules/performanceReviews/business/ports/performanceReviewCalibrationAudiencesPort';
import { PerformanceReviewCalibrationRepositoryPort } from '../../../src/api/modules/performanceReviews/business/ports/performanceReviewCalibrationRepositoryPort';
import { PerformanceReviewUsersPort } from '../../../src/api/modules/performanceReviews/business/ports/performanceReviewUsersPort';
import { PerformanceReviewCalibrationService } from '../../../src/api/modules/performanceReviews/business/services/performanceReviewCalibrationService';
import { PerformanceReviewsService } from '../../../src/api/modules/performanceReviews/business/services/performanceReviewsService';
import { PerformanceReviewCycleStatus } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewCycleStatus';
import { PerformanceReviewEvaluationTypes } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewEvaluationTypes';
import { PerformanceReviewFormShareStatus } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormShareStatus';
import { PerformanceReviewFormStatus } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormStatus';
import { isSubordinateReviewConfig } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormReviewConfiguration';
import {
    generatePerformanceReviewCalibrationRuleWithValues,
    generatePerformanceReviewCycleWithValues,
    generatePerformanceReviewFormReviewWithValues,
    generatePerformanceReviewFormTemplateWithValues,
} from './common';
import { fakePositiveInteger } from '../../common/common';

const calibrationFormReviewTestValues = {
    shareStatus: PerformanceReviewFormShareStatus.SHARE_DISABLED,
    status: PerformanceReviewFormStatus.PENDING,
    type: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW,
} as const;

let calibrationRepositoryMock: MockProxy<PerformanceReviewCalibrationRepositoryPort>;
let calibrationAudiencesMock: MockProxy<PerformanceReviewCalibrationAudiencesPort>;
let usersPortMock: MockProxy<PerformanceReviewUsersPort>;
let utilsPortMock: MockProxy<UtilsPort>;
let performanceReviewsServiceMock: MockProxy<PerformanceReviewsService>;

describe('PerformanceReviewCalibrationService', () => {
    let service: PerformanceReviewCalibrationService;
    let cycle: PerformanceReviewCycle;
    let instanceId: number;
    const validAudience = { conditions: [{ type: SegmentationType.ALL }] };

    beforeEach(() => {
        calibrationRepositoryMock = mock<PerformanceReviewCalibrationRepositoryPort>();
        calibrationAudiencesMock = mock<PerformanceReviewCalibrationAudiencesPort>();
        usersPortMock = mock<PerformanceReviewUsersPort>();
        utilsPortMock = mock<UtilsPort>();
        performanceReviewsServiceMock = mock<PerformanceReviewsService>();
        utilsPortMock.generateUUID.mockImplementation(() => faker.string.uuid());
        utilsPortMock.getAllIntersectionsOfArrays.mockImplementation(getAllIntersectionsOfArrays);

        service = new PerformanceReviewCalibrationService(
            calibrationRepositoryMock,
            calibrationAudiencesMock,
            usersPortMock,
            utilsPortMock,
        );
        service.setPerformanceReviewsService = performanceReviewsServiceMock;

        cycle = generatePerformanceReviewCycleWithValues(fakePositiveInteger(), new Date(), {
            instanceId,
            status: PerformanceReviewCycleStatus.IN_PROGRESS,
            formTemplateTypes: [PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW],
        });
        const subordinateTemplate = cycle.formTemplates?.find(
            (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
        );
        if (subordinateTemplate && isSubordinateReviewConfig(subordinateTemplate.formReviewConfig)) {
            subordinateTemplate.formReviewConfig.withCalibration = true;
        }

        instanceId = cycle.instanceId;

        performanceReviewsServiceMock.findCycleByPk.mockResolvedValue(cycle);
    });

    describe('createCalibrationRule', () => {
        it('should throw REVIEW_CYCLE_NOT_FOUND when cycle does not exist', async () => {
            performanceReviewsServiceMock.findCycleByPk.mockRejectedValue(
                new NotFoundError(NotFoundError.generateMessage('Review Cycle', cycle.id), ErrorCodes.REVIEW_CYCLE_NOT_FOUND),
            );
            await expect(
                service.createCalibrationRule(cycle.instanceId, cycle.id, { calibratorsAudienceQuery: validAudience, calibratedAudienceQuery: validAudience }),
            ).rejects.toMatchObject({ code: ErrorCodes.REVIEW_CYCLE_NOT_FOUND });
        });

        it.each([
            PerformanceReviewCycleStatus.FINISHED,
            PerformanceReviewCycleStatus.DRAFT,
            PerformanceReviewCycleStatus.PENDING,
            PerformanceReviewCycleStatus.SELECTION,
        ])('should throw REVIEW_CYCLE_INVALID_STATUS when cycle status is %s', async (status) => {
            cycle.status = status;
            await expect(
                service.createCalibrationRule(cycle.instanceId, cycle.id, { calibratorsAudienceQuery: validAudience, calibratedAudienceQuery: validAudience }),
            ).rejects.toMatchObject({ code: ErrorCodes.REVIEW_CYCLE_INVALID_STATUS });
        });

        it('should throw CALIBRATION_NOT_ENABLED when withCalibration is false', async () => {
            const subordinateTemplate = cycle.formTemplates?.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            );
            if (subordinateTemplate && isSubordinateReviewConfig(subordinateTemplate.formReviewConfig)) {
                subordinateTemplate.formReviewConfig.withCalibration = false;
            }
            await expect(
                service.createCalibrationRule(cycle.instanceId, cycle.id, { calibratorsAudienceQuery: validAudience, calibratedAudienceQuery: validAudience }),
            ).rejects.toMatchObject({ code: ErrorCodes.CALIBRATION_NOT_ENABLED });
        });

        it('should create rule when all validations pass', async () => {
            const expectedRule = { id: 'uuid', instanceId: cycle.instanceId, cycleId: cycle.id, calibratorsAudience: validAudience, calibratedAudience: validAudience };
            const subordinateTemplate = cycle.formTemplates!.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            )!;
            calibrationRepositoryMock.createRule.mockResolvedValue(expectedRule as never);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set<number>());
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);
            const result = await service.createCalibrationRule(cycle.instanceId, cycle.id, { calibratorsAudienceQuery: validAudience, calibratedAudienceQuery: validAudience });
            expect(result).toEqual(expectedRule);
            expect(calibrationRepositoryMock.createRule).toHaveBeenCalled();
            expect(calibrationAudiencesMock.upsertEntity).toHaveBeenCalledWith(expectedRule, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATORS);
        });

        it('should upsert both calibrators and calibrated segmentable entities', async () => {
            const expectedRule = {
                id: 'uuid-x',
                instanceId: cycle.instanceId,
                cycleId: cycle.id,
                calibratorsAudience: validAudience,
                calibratedAudience: validAudience,
            };
            const subordinateTemplate = cycle.formTemplates!.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            )!;
            calibrationRepositoryMock.createRule.mockResolvedValue(expectedRule as never);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set<number>());
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);

            await service.createCalibrationRule(cycle.instanceId, cycle.id, {
                calibratorsAudienceQuery: validAudience,
                calibratedAudienceQuery: validAudience,
            });

            expect(calibrationAudiencesMock.upsertEntity).toHaveBeenCalledWith(expectedRule, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATORS);
            expect(calibrationAudiencesMock.upsertEntity).toHaveBeenCalledWith(expectedRule, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATED);
        });
    });

    describe('calibration rule form review sync (incremental)', () => {
        it('should create FormReviews and FormReviewRules for matching collaborators that are cycle target users on create', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const targetUserId1 = fakePositiveInteger();
            const targetUserId2 = fakePositiveInteger();
            const nonTargetUserId = fakePositiveInteger();
            const subordinateTemplate = generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                withCalibration: true,
            });

            calibrationRepositoryMock.createRule.mockResolvedValue(rule);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set([targetUserId1, targetUserId2]));
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([targetUserId1, targetUserId2, nonTargetUserId]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);

            const createdFR1 = generatePerformanceReviewFormReviewWithValues(100, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: targetUserId1,
            });
            const createdFR2 = generatePerformanceReviewFormReviewWithValues(101, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: targetUserId2,
            });
            performanceReviewsServiceMock.createFormReviews.mockResolvedValue([createdFR1, createdFR2]);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);

            await service.createCalibrationRule(instanceId, cycle.id, {
                calibratorsAudienceQuery: rule.calibratorsAudience,
                calibratedAudienceQuery: rule.calibratedAudience,
            });

            expect(performanceReviewsServiceMock.createFormReviews).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({ reviewedId: targetUserId1, type: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW }),
                    expect.objectContaining({ reviewedId: targetUserId2, type: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW }),
                ]),
            );
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalled();
        });

        it('should delete orphaned unclaimed FormReviews after rule update narrows collaborators', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const keepUserId = fakePositiveInteger();
            const removedUserId = fakePositiveInteger();
            const subordinateTemplate = generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                withCalibration: true,
            });

            calibrationRepositoryMock.getRuleById.mockResolvedValue(rule);
            calibrationRepositoryMock.updateRule.mockImplementation(async (r) => r);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            calibrationRepositoryMock.deleteFormReviewRulesByRuleId.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set([keepUserId]));
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([keepUserId]);

            const existingKeep = generatePerformanceReviewFormReviewWithValues(200, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: keepUserId,
                reviewerId: null,
            });
            const existingRemoved = generatePerformanceReviewFormReviewWithValues(201, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: removedUserId,
                reviewerId: null,
            });
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([existingKeep, existingRemoved]);

            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([201]);
            calibrationRepositoryMock.deleteUnclaimedCalibrationFormReviews.mockResolvedValue(undefined);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);

            await service.updateCalibrationRule(instanceId, cycle.id, rule.id, {
                calibratorsAudienceQuery: rule.calibratorsAudience,
                calibratedAudienceQuery: rule.calibratedAudience,
            });

            expect(calibrationRepositoryMock.deleteUnclaimedCalibrationFormReviews).toHaveBeenCalledWith([201]);
            expect(calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds).toHaveBeenCalledWith(cycle.id);
        });

        it('should not delete claimed FormReviews when orphan cleanup runs after rule update', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const claimedUserId = fakePositiveInteger();
            const calibratorId = fakePositiveInteger();
            const subordinateTemplate = generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                withCalibration: true,
            });

            calibrationRepositoryMock.getRuleById.mockResolvedValue(rule);
            calibrationRepositoryMock.updateRule.mockImplementation(async (r) => r);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            calibrationRepositoryMock.deleteFormReviewRulesByRuleId.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set());
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([]);

            const claimedFormReview = generatePerformanceReviewFormReviewWithValues(300, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: claimedUserId,
                reviewerId: calibratorId,
            });
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([claimedFormReview]);

            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);

            await service.updateCalibrationRule(instanceId, cycle.id, rule.id, {
                calibratorsAudienceQuery: rule.calibratorsAudience,
                calibratedAudienceQuery: rule.calibratedAudience,
            });

            expect(calibrationRepositoryMock.deleteUnclaimedCalibrationFormReviews).not.toHaveBeenCalled();
        });
    });

    describe('getCalibrationRules', () => {
        it('should return empty array when there are no rules for the cycle', async () => {
            calibrationRepositoryMock.getRulesByCycleIdPaginated.mockResolvedValue({ totalCount: 0, rules: [] });

            const result = await service.getCalibrationRules(instanceId, cycle.id, { limit: 10, offset: 0 });

            expect(result.rules).toEqual([]);
            expect(result.totalCount).toBe(0);
            expect(calibrationAudiencesMock.getCalibratorUserIds).not.toHaveBeenCalled();
            expect(usersPortMock.getUsersByPks).not.toHaveBeenCalled();
        });

        it('should enrich rules with calibratorUsers and calibratorsCount', async () => {
            const userId1 = fakePositiveInteger();
            const userId2 = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });

            calibrationRepositoryMock.getRulesByCycleIdPaginated.mockResolvedValue({ totalCount: 1, rules: [rule] });
            calibrationAudiencesMock.getCalibratorUserIds.mockResolvedValue({ count: 10, userIds: [userId1, userId2] });
            usersPortMock.getUsersByPks.mockResolvedValue([
                { id: userId1, firstName: 'Ana', lastName: 'García', profilePicture: null } as never,
                { id: userId2, firstName: 'Luis', lastName: 'Pérez', profilePicture: 'https://example.com/pic.jpg' } as never,
            ]);

            const result = await service.getCalibrationRules(instanceId, cycle.id, { limit: 10, offset: 0 });

            expect(result.rules).toHaveLength(1);
            expect(result.rules[0].calibratorsCount).toBe(10);
            expect(result.rules[0].calibratorUsers).toEqual([
                { id: userId1, firstName: 'Ana', lastName: 'García', profilePicture: null },
                { id: userId2, firstName: 'Luis', lastName: 'Pérez', profilePicture: 'https://example.com/pic.jpg' },
            ]);
        });

        it('should pass CALIBRATOR_PREVIEW_LIMIT to getCalibratorUserIds', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });

            calibrationRepositoryMock.getRulesByCycleIdPaginated.mockResolvedValue({ totalCount: 1, rules: [rule] });
            calibrationAudiencesMock.getCalibratorUserIds.mockResolvedValue({ count: 0, userIds: [] });
            usersPortMock.getUsersByPks.mockResolvedValue([]);

            await service.getCalibrationRules(instanceId, cycle.id, { limit: 10, offset: 0 });

            expect(calibrationAudiencesMock.getCalibratorUserIds).toHaveBeenCalledWith(
                instanceId,
                rule.calibratorsAudience.conditions,
                6,
            );
        });

        it('should return empty firstName/lastName/profilePicture when user is not found', async () => {
            const missingUserId = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });

            calibrationRepositoryMock.getRulesByCycleIdPaginated.mockResolvedValue({ totalCount: 1, rules: [rule] });
            calibrationAudiencesMock.getCalibratorUserIds.mockResolvedValue({ count: 1, userIds: [missingUserId] });
            usersPortMock.getUsersByPks.mockResolvedValue([]);

            const result = await service.getCalibrationRules(instanceId, cycle.id, { limit: 10, offset: 0 });

            expect(result.rules[0].calibratorUsers).toEqual([
                { id: missingUserId, firstName: '', lastName: '', profilePicture: null },
            ]);
        });

        it('should batch all calibrator user IDs in a single getUsersByPks call across multiple rules', async () => {
            const userId1 = fakePositiveInteger();
            const userId2 = fakePositiveInteger();
            const rule1 = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const rule2 = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });

            calibrationRepositoryMock.getRulesByCycleIdPaginated.mockResolvedValue({ totalCount: 2, rules: [rule1, rule2] });
            calibrationAudiencesMock.getCalibratorUserIds
                .mockResolvedValueOnce({ count: 1, userIds: [userId1] })
                .mockResolvedValueOnce({ count: 1, userIds: [userId2] });
            usersPortMock.getUsersByPks.mockResolvedValue([
                { id: userId1, firstName: 'A', lastName: 'B', profilePicture: null } as never,
                { id: userId2, firstName: 'C', lastName: 'D', profilePicture: null } as never,
            ]);

            await service.getCalibrationRules(instanceId, cycle.id, { limit: 10, offset: 0 });

            expect(usersPortMock.getUsersByPks).toHaveBeenCalledTimes(1);
            expect(usersPortMock.getUsersByPks).toHaveBeenCalledWith(expect.arrayContaining([userId1, userId2]));
        });
    });

    describe('getCalibrationRule', () => {
        it('should throw NOT_FOUND when rule does not exist', async () => {
            calibrationRepositoryMock.getRuleById.mockResolvedValue(null);
            await expect(service.getCalibrationRule(cycle.instanceId, cycle.id, 'nonexistent')).rejects.toMatchObject({ code: ErrorCodes.NOT_FOUND });
        });

        it('should throw NOT_FOUND when rule belongs to a different cycle', async () => {
            calibrationRepositoryMock.getRuleById.mockResolvedValue({ id: 'uuid', cycleId: 999 } as never);
            await expect(service.getCalibrationRule(cycle.instanceId, cycle.id, 'uuid')).rejects.toMatchObject({ code: ErrorCodes.NOT_FOUND });
        });
    });

    describe('deleteCalibrationRule', () => {
        it('should delete rule when all validations pass', async () => {
            calibrationRepositoryMock.getRuleById.mockResolvedValue({ id: 'uuid', cycleId: cycle.id, instanceId: cycle.instanceId } as never);
            calibrationRepositoryMock.deleteFormReviewRulesByRuleId.mockResolvedValue(undefined);
            calibrationRepositoryMock.deleteRule.mockResolvedValue(undefined);
            calibrationAudiencesMock.deleteEntity.mockResolvedValue(undefined);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);
            await service.deleteCalibrationRule(cycle.instanceId, cycle.id, 'uuid');
            expect(calibrationRepositoryMock.deleteRule).toHaveBeenCalledWith('uuid', cycle.instanceId);
            expect(calibrationAudiencesMock.deleteEntity).toHaveBeenCalledWith('uuid', cycle.instanceId, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATORS);
        });

        it('should delete both calibrators and calibrated segmentable entities on delete', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId: cycle.instanceId });

            calibrationRepositoryMock.getRuleById.mockResolvedValue(rule);
            calibrationRepositoryMock.deleteFormReviewRulesByRuleId.mockResolvedValue(undefined);
            calibrationRepositoryMock.deleteRule.mockResolvedValue(undefined);
            calibrationAudiencesMock.deleteEntity.mockResolvedValue(undefined);
            calibrationAudiencesMock.deleteEntity.mockResolvedValue(undefined);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.deleteCalibrationRule(cycle.instanceId, cycle.id, rule.id);

            expect(calibrationAudiencesMock.deleteEntity).toHaveBeenCalledWith(rule.id, cycle.instanceId, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATORS);
            expect(calibrationAudiencesMock.deleteEntity).toHaveBeenCalledWith(rule.id, cycle.instanceId, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATED);
        });
    });

    describe('updateCalibrationRule', () => {
        it('should upsert both calibrators and calibrated segmentable entities on update', async () => {
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId: cycle.instanceId });
            const subordinateTemplate = cycle.formTemplates!.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            )!;

            calibrationRepositoryMock.getRuleById.mockResolvedValue(rule);
            calibrationRepositoryMock.updateRule.mockImplementation(async (r) => r);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            calibrationAudiencesMock.upsertEntity.mockResolvedValue(undefined);
            calibrationRepositoryMock.deleteFormReviewRulesByRuleId.mockResolvedValue(undefined);
            performanceReviewsServiceMock.findFormTemplateByEvaluationType.mockResolvedValue(subordinateTemplate);
            performanceReviewsServiceMock.getTargetUserIds.mockResolvedValue(new Set<number>());
            calibrationAudiencesMock.getCollaboratorUserIds.mockResolvedValue([]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.updateCalibrationRule(cycle.instanceId, cycle.id, rule.id, {
                calibratorsAudienceQuery: rule.calibratorsAudience,
                calibratedAudienceQuery: rule.calibratedAudience,
            });

            expect(calibrationAudiencesMock.upsertEntity).toHaveBeenCalledWith(rule, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATORS);
            expect(calibrationAudiencesMock.upsertEntity).toHaveBeenCalledWith(rule, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATED);
        });
    });

    describe('fillCalibration', () => {
        const calibratorId = fakePositiveInteger();
        const formReviewId = fakePositiveInteger();
        const fillParams = { fillingTime: 120, questionAnswers: [] };

        it('should claim and delegate to performanceReviewsService.fillReview on success', async () => {
            const formReview = generatePerformanceReviewFormReviewWithValues(formReviewId, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: fakePositiveInteger(),
                reviewerId: null,
            });

            calibrationRepositoryMock.getCalibrationFormReviewById.mockResolvedValue(formReview);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue(['rule-1']);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue(['rule-1']);
            calibrationRepositoryMock.claimCalibrationFormReview.mockResolvedValue({ hasClaimed: true });

            const subordinateTemplate = cycle.formTemplates!.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            )!;
            const filledReview = generatePerformanceReviewFormReviewWithValues(formReview.id, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: formReview.reviewedId,
                reviewerId: calibratorId,
                status: PerformanceReviewFormStatus.FINISHED,
            });
            performanceReviewsServiceMock.fillReview.mockResolvedValue(filledReview);

            const result = await service.fillCalibration(instanceId, cycle.id, formReviewId, calibratorId, fillParams);

            expect(calibrationRepositoryMock.claimCalibrationFormReview).toHaveBeenCalledWith(formReviewId, calibratorId);
            expect(performanceReviewsServiceMock.fillReview).toHaveBeenCalledWith(
                cycle,
                expect.objectContaining({ id: formReviewId, reviewerId: calibratorId }),
                subordinateTemplate,
                calibratorId,
                expect.objectContaining({ fillingTime: 120, goalCycleId: cycle.goalCycleId }),
            );
            expect(result).toEqual(filledReview);
        });

        it('should throw CALIBRATION_ALREADY_CLAIMED (409) on race condition (hasClaimed=false)', async () => {
            const formReview = generatePerformanceReviewFormReviewWithValues(formReviewId, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: fakePositiveInteger(),
                reviewerId: null,
            });

            calibrationRepositoryMock.getCalibrationFormReviewById.mockResolvedValue(formReview);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue(['rule-1']);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue(['rule-1']);
            calibrationRepositoryMock.claimCalibrationFormReview.mockResolvedValue({ hasClaimed: false });

            await expect(
                service.fillCalibration(instanceId, cycle.id, formReviewId, calibratorId, fillParams),
            ).rejects.toMatchObject({ code: ErrorCodes.CALIBRATION_ALREADY_CLAIMED });
        });

        it('should throw NOT_A_CALIBRATOR (403) when user has no matching rule IDs', async () => {
            const formReview = generatePerformanceReviewFormReviewWithValues(formReviewId, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: fakePositiveInteger(),
                reviewerId: null,
            });

            calibrationRepositoryMock.getCalibrationFormReviewById.mockResolvedValue(formReview);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([]);

            await expect(
                service.fillCalibration(instanceId, cycle.id, formReviewId, calibratorId, fillParams),
            ).rejects.toMatchObject({ code: ErrorCodes.NOT_A_CALIBRATOR });
        });
    });

    describe('createCalibrationFormReviewsForUser', () => {
        it('should do nothing when cycle has no calibration rules', async () => {
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([]);

            await service.createCalibrationFormReviewsForUser(cycle, fakePositiveInteger());

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).not.toHaveBeenCalled();
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
        });

        it('should do nothing when cycle has no subordinate template', async () => {
            const cycleWithoutTemplate = generatePerformanceReviewCycleWithValues(fakePositiveInteger(), new Date(), {
                instanceId,
                status: PerformanceReviewCycleStatus.IN_PROGRESS,
            });
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycleWithoutTemplate.id, instanceId });
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);

            await service.createCalibrationFormReviewsForUser(cycleWithoutTemplate, fakePositiveInteger());

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).not.toHaveBeenCalled();
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
        });

        it('should do nothing when subordinate template has withCalibration disabled', async () => {
            const subordinateTemplate = cycle.formTemplates?.find(
                (ft) => ft.evaluationType === PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
            );
            if (subordinateTemplate && isSubordinateReviewConfig(subordinateTemplate.formReviewConfig)) {
                subordinateTemplate.formReviewConfig.withCalibration = false;
            }
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);

            await service.createCalibrationFormReviewsForUser(cycle, fakePositiveInteger());

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).not.toHaveBeenCalled();
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
        });

        it('should do nothing when user is not in any rule collaborators audience', async () => {
            const userId = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([]);

            await service.createCalibrationFormReviewsForUser(cycle, userId);

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).not.toHaveBeenCalled();
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
        });

        it('should do nothing when form review already exists for the user (deduplication guard)', async () => {
            const userId = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([rule.id]);

            const existingFormReview = generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([existingFormReview]);

            await service.createCalibrationFormReviewsForUser(cycle, userId);

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).toHaveBeenCalledWith(cycle.id, [userId]);
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.createFormReviewRules).not.toHaveBeenCalled();
        });

        it('should create a form review and link it to all matching rules', async () => {
            const userId = fakePositiveInteger();
            const rule1 = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const rule2 = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const nonMatchingRule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule1, rule2, nonMatchingRule]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([rule1.id, rule2.id]);

            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);

            const createdFormReview = generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });
            performanceReviewsServiceMock.createFormReviews.mockResolvedValue([createdFormReview]);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);

            await service.createCalibrationFormReviewsForUser(cycle, userId);

            expect(calibrationRepositoryMock.getCalibrationFormReviewsForCycle).toHaveBeenCalledWith(cycle.id, [userId]);
            expect(performanceReviewsServiceMock.createFormReviews).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({ reviewedId: userId, type: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW }),
                ]),
            );
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({ formReviewId: createdFormReview.id, calibrationRuleId: rule1.id }),
                    expect.objectContaining({ formReviewId: createdFormReview.id, calibrationRuleId: rule2.id }),
                ]),
            );
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalledWith(
                expect.not.arrayContaining([expect.objectContaining({ calibrationRuleId: nonMatchingRule.id })]),
            );
        });

        it('should resolve matching rules via getRuleIdsWhereUserIs and not call getCollaboratorUserIds per rule', async () => {
            const userId = fakePositiveInteger();
            const ruleA = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const ruleB = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const ruleOther = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id + 1, instanceId });

            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([ruleA, ruleB]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([ruleA.id, ruleOther.id]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);

            const createdFR = generatePerformanceReviewFormReviewWithValues(900, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });
            performanceReviewsServiceMock.createFormReviews.mockResolvedValue([createdFR]);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);

            await service.createCalibrationFormReviewsForUser(cycle, userId);

            expect(calibrationAudiencesMock.getRuleIdsWhereUserIs).toHaveBeenCalledWith(userId, instanceId, SEGMENTABLE_TYPES.CALIBRATION_RULE_CALIBRATED);
            expect(calibrationAudiencesMock.getCollaboratorUserIds).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({ formReviewId: 900, calibrationRuleId: ruleA.id }),
                ]),
            );
            // ruleOther is not in this cycle, ruleB does not match the user → only ruleA is linked
            const linked = calibrationRepositoryMock.createFormReviewRules.mock.calls[0][0];
            expect(linked).toHaveLength(1);
        });
    });

    describe('syncCalibratedRulesForUser', () => {
        it('should be a no-op when there are no in-progress cycles for the user', async () => {
            const userId = fakePositiveInteger();
            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(calibrationAudiencesMock.getRuleIdsWhereUserIs).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.getRulesByCycleId).not.toHaveBeenCalled();
            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.createFormReviewRules).not.toHaveBeenCalled();
        });

        it('should create FormReview + form review rule when user newly matches a rule and no FormReview exists', async () => {
            const userId = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            cycle.formTemplates = [
                generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                    evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                    withCalibration: true,
                }),
            ];
            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([cycle]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([rule.id]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([]);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue([]);
            const created = generatePerformanceReviewFormReviewWithValues(500, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });
            performanceReviewsServiceMock.createFormReviews.mockResolvedValue([created]);
            calibrationRepositoryMock.createFormReviewRules.mockResolvedValue(undefined);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(performanceReviewsServiceMock.createFormReviews).toHaveBeenCalledWith(
                expect.arrayContaining([
                    expect.objectContaining({ reviewedId: userId, type: PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW }),
                ]),
            );
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalledWith([
                expect.objectContaining({ formReviewId: 500, calibrationRuleId: rule.id, instanceId }),
            ]);
            expect(calibrationRepositoryMock.deleteFormReviewRules).not.toHaveBeenCalled();
        });

        it('should add only the new form review rule when an existing FormReview already covers the user', async () => {
            const userId = fakePositiveInteger();
            const ruleExisting = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            const ruleNew = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            cycle.formTemplates = [
                generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                    evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                    withCalibration: true,
                }),
            ];
            const formReview = generatePerformanceReviewFormReviewWithValues(600, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });
            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([cycle]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([ruleExisting.id, ruleNew.id]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([ruleExisting, ruleNew]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([formReview]);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue([ruleExisting.id]);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.createFormReviewRules).toHaveBeenCalledWith([
                expect.objectContaining({ formReviewId: 600, calibrationRuleId: ruleNew.id }),
            ]);
            expect(calibrationRepositoryMock.deleteFormReviewRules).not.toHaveBeenCalled();
        });

        it('should remove the form review rule and clean up an unclaimed orphan FormReview when the user no longer matches any rule', async () => {
            const userId = fakePositiveInteger();
            const ruleOld = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            cycle.formTemplates = [
                generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                    evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                    withCalibration: true,
                }),
            ];
            const formReview = generatePerformanceReviewFormReviewWithValues(700, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
                reviewerId: null,
            });
            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([cycle]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([ruleOld]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([formReview]);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue([ruleOld.id]);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([700]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(calibrationRepositoryMock.deleteFormReviewRules).toHaveBeenCalledWith(700, [ruleOld.id]);
            expect(calibrationRepositoryMock.deleteUnclaimedCalibrationFormReviews).toHaveBeenCalledWith([700]);
        });

        it('should preserve a claimed FormReview when the user no longer matches any rule', async () => {
            const userId = fakePositiveInteger();
            const claimedReviewerId = fakePositiveInteger();
            const ruleOld = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            cycle.formTemplates = [
                generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                    evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                    withCalibration: true,
                }),
            ];
            const formReview = generatePerformanceReviewFormReviewWithValues(800, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
                reviewerId: claimedReviewerId,
            });
            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([cycle]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([ruleOld]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([formReview]);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue([ruleOld.id]);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(calibrationRepositoryMock.deleteFormReviewRules).toHaveBeenCalledWith(800, [ruleOld.id]);
            expect(calibrationRepositoryMock.deleteUnclaimedCalibrationFormReviews).not.toHaveBeenCalled();
        });

        it('should be a no-op on the second consecutive call (idempotent)', async () => {
            const userId = fakePositiveInteger();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ cycleId: cycle.id, instanceId });
            cycle.formTemplates = [
                generatePerformanceReviewFormTemplateWithValues(cycle.id, {
                    evaluationType: PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW,
                    withCalibration: true,
                }),
            ];
            const formReview = generatePerformanceReviewFormReviewWithValues(900, cycle.id, {
                ...calibrationFormReviewTestValues,
                reviewedId: userId,
            });

            calibrationRepositoryMock.getInProgressCalibrationCyclesForUser.mockResolvedValue([cycle]);
            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([rule.id]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationRepositoryMock.getCalibrationFormReviewsForCycle.mockResolvedValue([formReview]);
            calibrationRepositoryMock.getFormReviewRuleIds.mockResolvedValue([rule.id]);
            calibrationRepositoryMock.getOrphanedUnclaimedCalibrationFormReviewIds.mockResolvedValue([]);

            await service.syncCalibratedRulesForUser(instanceId, userId);

            expect(performanceReviewsServiceMock.createFormReviews).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.createFormReviewRules).not.toHaveBeenCalled();
            expect(calibrationRepositoryMock.deleteFormReviewRules).not.toHaveBeenCalled();
        });
    });

    describe('listVisibleCalibratedUsers', () => {
        const paginationOptions = { limit: 20, offset: 0 };

        it('should return calibrated users filtered by calibrator rule IDs', async () => {
            const calibratorId = fakePositiveInteger();
            const ruleId = faker.string.uuid();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ id: ruleId, cycleId: cycle.id, instanceId });
            const expectedResult = {
                totalCount: 1,
                calibratedUsers: [{ formReviewId: fakePositiveInteger(), reviewerId: null, user: { userId: fakePositiveInteger(), firstName: 'John', lastName: 'Doe', profilePicture: null } }],
            };

            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([ruleId]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationRepositoryMock.getCalibratedUsers.mockResolvedValue(expectedResult);

            const result = await service.listVisibleCalibratedUsers(instanceId, cycle.id, calibratorId, paginationOptions);

            expect(result).toEqual(expectedResult);
            expect(calibrationRepositoryMock.getCalibratedUsers).toHaveBeenCalledWith(cycle.id, [ruleId], calibratorId, paginationOptions);
        });

        it('should return empty result when user has no calibrator rule IDs', async () => {
            const calibratorId = fakePositiveInteger();

            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([]);

            const result = await service.listVisibleCalibratedUsers(instanceId, cycle.id, calibratorId, paginationOptions);

            expect(result).toEqual({ totalCount: 0, calibratedUsers: [] });
            expect(calibrationRepositoryMock.getCalibratedUsers).not.toHaveBeenCalled();
        });

        it('should return empty result when no rules match the cycle', async () => {
            const calibratorId = fakePositiveInteger();
            const ruleId = faker.string.uuid();

            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([ruleId]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([]);

            const result = await service.listVisibleCalibratedUsers(instanceId, cycle.id, calibratorId, paginationOptions);

            expect(result).toEqual({ totalCount: 0, calibratedUsers: [] });
            expect(calibrationRepositoryMock.getCalibratedUsers).not.toHaveBeenCalled();
        });

        it('should pass answered and q filters to the repository', async () => {
            const calibratorId = fakePositiveInteger();
            const ruleId = faker.string.uuid();
            const rule = generatePerformanceReviewCalibrationRuleWithValues({ id: ruleId, cycleId: cycle.id, instanceId });
            const optionsWithFilters = { limit: 10, offset: 0, q: 'Juan', answered: false };

            calibrationAudiencesMock.getRuleIdsWhereUserIs.mockResolvedValue([ruleId]);
            calibrationRepositoryMock.getRulesByCycleId.mockResolvedValue([rule]);
            calibrationRepositoryMock.getCalibratedUsers.mockResolvedValue({ totalCount: 0, calibratedUsers: [] });

            await service.listVisibleCalibratedUsers(instanceId, cycle.id, calibratorId, optionsWithFilters);

            expect(calibrationRepositoryMock.getCalibratedUsers).toHaveBeenCalledWith(cycle.id, [ruleId], calibratorId, optionsWithFilters);
        });
    });
});
