import { faker } from '@faker-js/faker/locale/en_US';
import { UserStatus } from '@humand-packages/common';
import { CoreForm } from '../../../src/api/kernels/forms/business/models/coreForm';
import { FormAnswer } from '../../../src/api/kernels/forms/business/models/formAnswer';
import { CoreFormQuestion, FormQuestionWithAnswer } from '../../../src/api/kernels/forms/business/types/coreFormContent';
import { QuestionAnswer } from '../../../src/api/kernels/forms/business/types/formAnswer';
import { QUESTION_COMMENTABLE_STATUS } from '../../../src/api/kernels/forms/business/types/questionCommentableStatus';
import { SegmentationType } from '../../../src/api/modules/audiences/business/constants/segmentationTypes';
import { SegmentationExpressionConditions } from '../../../src/api/modules/audiences/business/interfaces/segmentationExpressionConditions';
import { FormTemplate } from '../../../src/api/modules/formTemplates/business/models/formTemplate';
import { FORM_TEMPLATE_ORIGINS } from '../../../src/api/modules/formTemplates/business/types/origins';
import { FORM_TEMPLATE_STATUS, FormTemplateStatusValues } from '../../../src/api/modules/formTemplates/business/types/status';
import { FormTemplateDTO } from '../../../src/api/modules/formTemplates/presentation/dataTransferObjects/formTemplateDTO';
import { FormTemplateWithQuestionTemplatesDTO } from '../../../src/api/modules/formTemplates/presentation/dataTransferObjects/formTemplateWithTemplateQuestionsDTO';
import { GoalDTO } from '../../../src/api/modules/goals/presentation/dataTransferObjects/goalDTO';
import { DuplicatePerformanceReviewCycleInterface } from '../../../src/api/modules/performanceReviews/business/interfaces/duplicatePerformanceReviewCycleInterface';
import { DuplicatePerformanceReviewFormReviewInterface } from '../../../src/api/modules/performanceReviews/business/interfaces/duplicatePerformanceReviewFormReviewInterface';
import { DuplicatePerformanceReviewFormTemplateInterface } from '../../../src/api/modules/performanceReviews/business/interfaces/duplicatePerformanceReviewFormTemplateInterface';
import { InstanceInformationForReviews } from '../../../src/api/modules/performanceReviews/business/interfaces/performanceReviewInformationForReviews';
import { UpsertFormTemplateInterface } from '../../../src/api/modules/performanceReviews/business/interfaces/upsertFormTemplateInterface';
import { PerformanceReviewCalibrationRule } from '../../../src/api/modules/performanceReviews/business/models/performanceReviewCalibrationRule';
import {
    PerformanceReviewCycle,
    PerformanceReviewCycleForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewCycle';
import {
    PerformanceReviewExternalReviewer,
    PerformanceReviewExternalReviewerForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/PerformanceReviewExternalReviewer';
import {
    PerformanceReviewFormReview,
    PerformanceReviewFormReviewForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewFormReview';
import {
    PerformanceReviewFormReviewEditRequest,
    PerformanceReviewFormReviewEditRequestForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewFormReviewEditRequest';
import {
    PerformanceReviewFormTemplate,
    PerformanceReviewFormTemplateForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewFormTemplate';
import {
    PerformanceReviewGoalQuestionWithAnswer,
    PerformanceReviewGoalQuestionWithAnswerDataFields,
    PerformanceReviewGoalQuestionWithAnswerForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewGoalQuestionWithAnswer';
import { PerformanceReviewNineBoxPoint } from '../../../src/api/modules/performanceReviews/business/models/PerformanceReviewNineBoxPoint';
import {
    PerformanceReviewReviewer,
    PerformanceReviewReviewerForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewReviewer';
import {
    PerformanceReviewTargetUser,
    PerformanceReviewTargetUserForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewTargetUser';
import {
    PerformanceReviewTemplate,
    PerformanceReviewTemplateForCreation,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewTemplate';
import {
    PerformanceReviewUser,
    PerformanceReviewUserWithStats,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewUser';
import {
    PerformanceReviewCycleReviewedUsersMode,
    PerformanceReviewCycleReviewedUsersModeValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewCycleReviewedUsersMode';
import {
    PerformanceReviewCycleStatus,
    PerformanceReviewCycleStatusValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewCycleStatus';
import {
    PerformanceReviewEvaluationTypes,
    PerformanceReviewEvaluationTypesValues,
    PerformanceReviewPeerEvaluationTypes,
    PerformanceReviewPeerEvaluationTypesValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewEvaluationTypes';
import { PerformanceReviewFormEditRequestStatus } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormEditRequestStatus';
import {
    FormReviewConfig,
    isSubordinateReviewConfig,
    PeerSelectionMode,
    ScoreConfig,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormReviewConfiguration';
import {
    PerformanceReviewFormShareStatus,
    PerformanceReviewFormShareStatusValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormShareStatus';
import {
    ReviewStatsByShareStatus,
    ReviewStatsByTypeAndStatus,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormStats';
import {
    PerformanceReviewFormStatus,
    PerformanceReviewFormStatusValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormStatus';
import { PerformanceReviewSelectionStats } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewSelectionStats';
import {
    PerformanceReviewTemplateGoalsProgressMode,
    PerformanceReviewTemplateGoalsProgressModeValues,
} from '../../../src/api/modules/performanceReviews/business/types/performanceReviewTemplateGoalsProgressMode';
import { PerformanceReviewUserStats, UserStatsByType } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewUserStats';
import { PerformanceReviewUserStatus } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewUserStatus';
import { PerformanceReviewCycleDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewCycleDAO';
import { PerformanceReviewExternalReviewerDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewExternalReviewerDAO';
import { PerformanceReviewFormReviewDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewDAO';
import { PerformanceReviewFormReviewEditRequestDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormReviewEditRequestDAO';
import { PerformanceReviewFormTemplateDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewFormTemplateDAO';
import { PerformanceReviewGoalQuestionWithAnswerDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewGoalQuestionWithAnswerDAO';
import { PerformanceReviewReviewerDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewReviewerDAO';
import { PerformanceReviewTargetUserDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTargetUserDAO';
import { PerformanceReviewTemplateDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewTemplateDAO';
import { PerformanceReviewUserDAO } from '../../../src/api/modules/performanceReviews/infrastructure/dataAccessObjects/performanceReviewUserDAO';
import {
    PerformanceReviewCompetencyQuestionWithAnswerDTO,
    PerformanceReviewCoreFormContentDTO,
    PerformanceReviewCoreFormQuestionDTO,
    PerformanceReviewGoalQuestionWithAnswerDTO,
    isCompetencyQuestionWithAnswer,
    isGoalQuestionWithAnswer,
    isPerformanceReviewCoreFormQuestion,
} from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewCoreFormDTO';
import {
    PerformanceReviewCycleDTO,
    PerformanceReviewCycleWithUserReviewsDTO,
} from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewCycleDTO';
import { PerformanceReviewExternalReviewerDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewExternalReviewerDTO';
import {
    PerformanceReviewFormReviewDTO,
    PerformanceReviewFormReviewWithFormDetailsDTO,
} from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewFormReviewDTO';
import { PerformanceReviewFormReviewEditRequestDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/PerformanceReviewFormReviewEditRequestDTO';
import { PerformanceReviewFormTemplateDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewFormTemplateDTO';
import { PerformanceReviewFormTemplateWithScoringInfoDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewFormTemplateWithScoringInfoDTO';
import { PerformanceReviewNineBoxPointDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewNormalizedNineBoxPointDTO';
import { PerformanceReviewReviewedDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewReviewedDTO';
import { PerformanceReviewReviewerDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewReviewerDTO';
import { PerformanceReviewSelectionStatsDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewSelectionStatsDTO';
import {
    PerformanceReviewTargetUserDTO,
    PerformanceReviewTargetUserWithUserReviewsDTO,
} from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewTargetUserDTO';
import { PerformanceReviewTemplateDTO } from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewTemplateDTO';
import {
    PerformanceReviewUserDTO,
    PerformanceReviewUserWithStatsDTO,
} from '../../../src/api/modules/performanceReviews/presentation/dataTransferObjects/performanceReviewUserDTO';
import { ScoreDisplayType, ScoreDisplayTypeValues } from '../../../src/api/modules/performanceReviews/business/types/performanceReviewFormReviewConfiguration';
import {
    QualitativeResultRangeVC
} from '../../../src/api/modules/performanceReviews/presentation/validationClasses/performanceReviewScoreConfigVC';
import { QuestionTemplate } from '../../../src/api/modules/questionTemplates/business/models/questionTemplate';
import { QuestionTemplateDTO } from '../../../src/api/modules/questionTemplates/presentation/dataTransferObjects/questionTemplateDTO';
import { User } from '../../../src/api/modules/users/business/models/user';
import { QUESTION_TYPES, isChoiceQuestionType } from '../../../src/api/types/questionTypes';
import { fakePositiveInteger } from '../../common/common';
import { generateRandomOriginalAnswerForQuestion } from '../../common/forms/questionTypes';
import { generateFormDraftWithQuestions } from '../../kernels/forms/common';
import { checkFormTemplateCreatorSerialized, createFormTemplateDAO } from '../formTemplates/common';
import { checkGoalSerialized, generateRandomGoal } from '../goals/common';
import { createQuestionTemplateDAO, generateQuestionTemplateWithValues } from '../questionTemplates/common';
import { ScaleDetailsVC, ScoreDetailsVC, WeightDetailsVC } from '../../../src/api/modules/performanceReviews/presentation/validationClasses/performanceReviewScoreConfigVC';
import { WeightDetails } from '../../../src/api/modules/performanceReviews/business/interfaces/performanceReviewFormTemplateInterface';
import {
    PerformanceReviewCompetencyQuestionWithAnswer,
    PerformanceReviewCompetencyQuestionWithAnswerDataFields,
} from '../../../src/api/modules/performanceReviews/business/models/performanceReviewCompetencyQuestionWithAnswer';
import { CompetencyDTO } from '../../../src/api/modules/competencies/presentation/dataTransferObjects/competencyDTO';
import { checkCompetencySerialized } from '../competencies/common';

export const generateRandomPerformanceReviewCycleForCreation = () => {
    return new PerformanceReviewCycleForCreation({
        canBeShared: faker.datatype.boolean(),
        duplicationId: faker.string.uuid(),
        endDate: faker.date.recent(),
        instanceId: fakePositiveInteger(),
        name: faker.word.words(),
        peerExternalReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewExternalReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        peerReviewReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        reviewedUsersMode: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleReviewedUsersMode)),
        reviewerLimitByHiringDate: faker.date.recent(),
        reviewsVisibleAfterFinishedReviewCycle: faker.datatype.boolean(),
        startDate: faker.date.recent(),
        status: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleStatus)),
        upwardReviewAnonymousAnswers: null,
        withSummary: faker.datatype.boolean(),
    });
};

export const generatePerformanceReviewCycleFromCreation = (instanceId: number, performanceReviewForCreation: PerformanceReviewCycleForCreation) => {
    return new PerformanceReviewCycle({
        canBeShared: performanceReviewForCreation.canBeShared,
        createdAt: faker.date.recent(),
        duplicationId: performanceReviewForCreation.duplicationId,
        endDate: performanceReviewForCreation.endDate,
        goalCycleId: performanceReviewForCreation.goalCycleId,
        id: fakePositiveInteger(),
        instanceId,
        name: performanceReviewForCreation.name,
        peerExternalReviewAnonymousAnswers: performanceReviewForCreation.peerExternalReviewAnonymousAnswers,
        peerReviewAnonymousAnswers: performanceReviewForCreation.peerReviewAnonymousAnswers,
        peerReviewExternalReviewerSelectionVisibleForBosses: performanceReviewForCreation.peerReviewExternalReviewerSelectionVisibleForBosses,
        peerReviewReviewerSelectionVisibleForBosses: performanceReviewForCreation.peerReviewReviewerSelectionVisibleForBosses,
        reviewedUsersMode: performanceReviewForCreation.reviewedUsersMode,
        reviewerLimitByHiringDate: performanceReviewForCreation.reviewerLimitByHiringDate,
        reviewsVisibleAfterFinishedReviewCycle: performanceReviewForCreation.reviewsVisibleAfterFinishedReviewCycle,
        startDate: performanceReviewForCreation.startDate,
        status: performanceReviewForCreation.status,
        updatedAt: faker.date.recent(),
        upwardReviewAnonymousAnswers: performanceReviewForCreation.upwardReviewAnonymousAnswers,
        withSummary: performanceReviewForCreation.withSummary,
    });
};

export const generateRandomPerformanceReviewCycle = (values?: { id?: number,reviewsVisibleAfterFinishedReviewCycle?: boolean}) => {
    return new PerformanceReviewCycle({
        canBeShared: faker.datatype.boolean(),
        createdAt: faker.date.recent(),
        duplicationId: faker.string.uuid(),
        endDate: faker.date.recent(),
        goalCycleId: fakePositiveInteger(),
        hasPendingReviews: faker.datatype.boolean(),
        id: values?.id ?? fakePositiveInteger(),
        instanceId: fakePositiveInteger(),
        name: faker.word.words(),
        peerExternalReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewExternalReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        peerReviewReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        reviewedUsersMode: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleReviewedUsersMode)),
        reviewerLimitByHiringDate: faker.date.recent(),
        reviewsVisibleAfterFinishedReviewCycle: values?.reviewsVisibleAfterFinishedReviewCycle ?? faker.datatype.boolean(),
        startDate: faker.date.recent(),
        status: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleStatus)),
        updatedAt: faker.date.recent(),
        upwardReviewAnonymousAnswers: null,
        withSummary: faker.datatype.boolean(),
    });
};

export const generatePerformanceReviewCycleWithValues = (
    id: number,
    date: Date,
    values?: {
        formTemplateTypes?: PerformanceReviewEvaluationTypesValues[];
        instanceId?: number;
        needsChanges?: boolean;
        status?: PerformanceReviewCycleStatusValues;
        targetUsers?: number[];
        usersMode?: PerformanceReviewCycleReviewedUsersModeValues;
        withSummary?: boolean;
        peerExternalReviewAnonymousAnswers?: boolean;
        peerReviewAnonymousAnswers?: boolean;
        peerReviewExternalReviewerSelectionVisibleForBosses?: boolean;
        peerReviewReviewerSelectionVisibleForBosses?: boolean;
        reviewsVisibleAfterFinishedReviewCycle?: boolean;
        reviewerLimitByHiringDate?: Date;
        canBeShared?: boolean;
        upwardReviewAnonymousAnswers?: boolean | null;
    },
) => {
    const valuesWithDefaults = {
        canBeShared: values?.canBeShared ?? faker.datatype.boolean(),
        formTemplateTypes: values?.formTemplateTypes ?? undefined,
        instanceId: values?.instanceId ?? fakePositiveInteger(),
        needsChanges: values?.needsChanges ?? null,
        peerExternalReviewAnonymousAnswers: values?.peerExternalReviewAnonymousAnswers ?? false,
        peerReviewAnonymousAnswers: values?.peerReviewAnonymousAnswers ?? false,
        peerReviewExternalReviewerSelectionVisibleForBosses: values?.peerReviewExternalReviewerSelectionVisibleForBosses ?? false,
        peerReviewReviewerSelectionVisibleForBosses: values?.peerReviewReviewerSelectionVisibleForBosses ?? false,
        reviewerLimitByHiringDate: values?.reviewerLimitByHiringDate ?? null,
        reviewsVisibleAfterFinishedReviewCycle: values?.reviewsVisibleAfterFinishedReviewCycle ?? false,
        status: values?.status ?? faker.helpers.arrayElement(Object.values(PerformanceReviewCycleStatus)),
        targetUsers: values?.targetUsers ?? undefined,
        upwardReviewAnonymousAnswers: values?.upwardReviewAnonymousAnswers ?? null,
        usersMode: values?.usersMode ?? faker.helpers.arrayElement(Object.values(PerformanceReviewCycleReviewedUsersMode)),
        withSummary: values?.withSummary ?? false,
    };
    const reviewCycle = new PerformanceReviewCycle({
        canBeShared: valuesWithDefaults.canBeShared ?? faker.datatype.boolean(),
        createdAt: date,
        duplicationId: faker.string.uuid(),
        endDate: null,
        goalCycleId: fakePositiveInteger(),
        id,
        instanceId: valuesWithDefaults.instanceId,
        name: faker.word.words(),
        needsChanges: valuesWithDefaults.needsChanges,
        peerExternalReviewAnonymousAnswers: valuesWithDefaults.peerExternalReviewAnonymousAnswers,
        peerReviewAnonymousAnswers: valuesWithDefaults.peerReviewAnonymousAnswers,
        peerReviewExternalReviewerSelectionVisibleForBosses: valuesWithDefaults.peerReviewExternalReviewerSelectionVisibleForBosses,
        peerReviewReviewerSelectionVisibleForBosses: valuesWithDefaults.peerReviewReviewerSelectionVisibleForBosses,
        reviewedUsersMode: valuesWithDefaults.usersMode,
        reviewerLimitByHiringDate: valuesWithDefaults.reviewerLimitByHiringDate,
        reviewsVisibleAfterFinishedReviewCycle: valuesWithDefaults.reviewsVisibleAfterFinishedReviewCycle,
        startDate: null,
        status: valuesWithDefaults.status,
        updatedAt: date,
        upwardReviewAnonymousAnswers: valuesWithDefaults.upwardReviewAnonymousAnswers,
        withSummary: valuesWithDefaults.withSummary,
    });
    reviewCycle.formTemplates =
        valuesWithDefaults.formTemplateTypes?.map((evaluationType) =>
            generateRandomPerformanceReviewFormTemplateWithTemplate(reviewCycle.id, reviewCycle.instanceId, { evaluationType }),
        ) ?? null;
    reviewCycle.targetUsers =
        valuesWithDefaults.targetUsers?.map((userId) => generateRandomPerformanceReviewTargetUser(reviewCycle.id, userId)) ?? null;
    return reviewCycle;
};

export const generatePerformanceReviewCalibrationRuleWithValues = (
    values?: {
        calibratorsAudience?: SegmentationExpressionConditions;
        calibratedAudience?: SegmentationExpressionConditions;
        createdAt?: Date;
        cycleId?: number;
        id?: string;
        instanceId?: number;
        updatedAt?: Date;
    },
) => {
    const defaultAudience: SegmentationExpressionConditions = { conditions: [{ type: SegmentationType.ALL }] };
    const valuesWithDefaults = {
        calibratorsAudience: values?.calibratorsAudience ?? defaultAudience,
        calibratedAudience: values?.calibratedAudience ?? defaultAudience,
        createdAt: values?.createdAt ?? new Date(),
        cycleId: values?.cycleId ?? fakePositiveInteger(),
        id: values?.id ?? faker.string.uuid(),
        instanceId: values?.instanceId ?? fakePositiveInteger(),
        updatedAt: values?.updatedAt ?? new Date(),
    };
    return new PerformanceReviewCalibrationRule({
        calibratorsAudience: valuesWithDefaults.calibratorsAudience,
        calibratedAudience: valuesWithDefaults.calibratedAudience,
        createdAt: valuesWithDefaults.createdAt,
        cycleId: valuesWithDefaults.cycleId,
        id: valuesWithDefaults.id,
        instanceId: valuesWithDefaults.instanceId,
        updatedAt: valuesWithDefaults.updatedAt,
    });
};

export const generateRandomPerformanceReviewFormTemplateForCreation = (cycleId: number) => {
    const evaluationType = faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes));
    return new PerformanceReviewFormTemplateForCreation({
        coreFormId: fakePositiveInteger(),
        coreFormTag: faker.string.uuid(),
        cycleId,
        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formReviewConfig: PerformanceReviewFormTemplate.getDefaultFormReviewConfig(evaluationType),
        scoreConfig: PerformanceReviewFormTemplate.getDefaultScoreConfig(),
        templateId: fakePositiveInteger(),
    });
};

export const generateRandomFormReviewConfig = (evaluationType?: PerformanceReviewEvaluationTypesValues) => {
    let formReviewConfig: FormReviewConfig | null;
    if (!evaluationType) {
        evaluationType = faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes));
    }
    switch (evaluationType) {
        case PerformanceReviewEvaluationTypes.AUTO_REVIEW:
            formReviewConfig = null;
            break;
        case PerformanceReviewEvaluationTypes.PEER_EXTERNAL_REVIEW:
            formReviewConfig = {
                anonymousAnswers: faker.datatype.boolean(),
                selectionMode: faker.helpers.arrayElement(Object.values(PeerSelectionMode)),
            }
            break;
        case PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW:
            formReviewConfig = {
                canBeShared: faker.datatype.boolean(),
                reviewsVisibleAfterFinishedReviewCycle: faker.datatype.boolean(),
                canBeSharedBeforeCycleFinish: faker.datatype.boolean(),
                withCalibration: faker.datatype.boolean(),
            };
            break;
        case PerformanceReviewEvaluationTypes.BOSS_REVIEW:
            formReviewConfig = {
                anonymousAnswers: faker.datatype.boolean(),
                reviewerLimitByHiringDate: null,
            }
            break;

        case PerformanceReviewEvaluationTypes.PEER_REVIEW:
            formReviewConfig = {
                anonymousAnswers: faker.datatype.boolean(),
                selectionMode: faker.helpers.arrayElement(Object.values(PeerSelectionMode)),
            }
            break;
        case PerformanceReviewEvaluationTypes.OTHERS_REVIEW:
            formReviewConfig = {
                anonymousAnswers: faker.datatype.boolean(),
                selectionMode: faker.helpers.arrayElement(Object.values(PeerSelectionMode)),
            }
            break;
        case PerformanceReviewEvaluationTypes.CALIBRATION_REVIEW:
            formReviewConfig = {}
            break;
        default:
            throw new Error(`Invalid evaluation type: ${evaluationType}`);
    }
    return formReviewConfig;
};

export const generateRandomReviewCycleDuplication = (): DuplicatePerformanceReviewCycleInterface => {
    return {
        canBeShared: faker.datatype.boolean(),
        duplicationId: faker.string.uuid(),
        endDate: faker.date.recent(),
        goalCycleId: fakePositiveInteger(),
        name: faker.word.words(),
        peerExternalReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewAnonymousAnswers: faker.datatype.boolean(),
        peerReviewExternalReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        peerReviewReviewerSelectionVisibleForBosses: faker.datatype.boolean(),
        reviewedUsersMode: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleReviewedUsersMode)),
        reviewerLimitByHiringDate: faker.date.recent(),
        reviewsVisibleAfterFinishedReviewCycle: faker.datatype.boolean(),
        startDate: faker.date.recent(),
        status: faker.helpers.arrayElement(Object.values(PerformanceReviewCycleStatus)),
        upwardReviewAnonymousAnswers: null,
        withSummary: faker.datatype.boolean(),
    };
};

export const generateRandomReviewDuplication = (): DuplicatePerformanceReviewFormReviewInterface => {
    return generatePerformanceReviewFormReviewWithValues(fakePositiveInteger(), fakePositiveInteger());
};

export const generateFormTemplateDuplication = (): DuplicatePerformanceReviewFormTemplateInterface => {
    return generateRandomPerformanceReviewFormTemplate(fakePositiveInteger());
};

export const generateRandomPerformanceReviewFormTemplate = (cycleId: number) => {
    return generatePerformanceReviewFormTemplateWithValues(cycleId);
};

export const generatePerformanceReviewFormTemplateWithValues = (
    cycleId: number,
    values?: {
        competenciesWeight?: number;
        evaluationType?: PerformanceReviewEvaluationTypesValues;
        formTemplateId?: number | null;
        formWeight?: number;
        goalsWeight?: number;
        scaleEnd?: number;
        scaleStart?: number;
        templateId?: number | null;
        withCalibration?: boolean;
        withCompetenciesScore?: boolean;
        withGoalScore?: boolean;
        withHiddenScoreForReviewed?: boolean;
        withScore?: boolean;
    },
) => {

    const defaultScoreConfig = PerformanceReviewFormTemplate.getDefaultScoreConfig();

    const valuesWithDefaults = {
        evaluationType: values?.evaluationType ?? faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formWeight: values?.formWeight ?? defaultScoreConfig.formWeight,
        competenciesWeight: values?.competenciesWeight ?? defaultScoreConfig.competenciesWeight,
        goalsWeight: values?.goalsWeight ?? defaultScoreConfig.goalsWeight,
        scaleEnd: values?.scaleEnd ?? defaultScoreConfig.scaleEnd,
        scaleStart: values?.scaleStart ?? defaultScoreConfig.scaleStart,
        templateId: values?.templateId !== undefined ? values?.templateId : fakePositiveInteger(),
        withGoalScore: values?.withGoalScore ?? defaultScoreConfig.withGoalScore,
        withCompetenciesScore: values?.withCompetenciesScore ?? defaultScoreConfig.withCompetenciesScore,
        withHiddenScoreForReviewed: values?.withHiddenScoreForReviewed ?? defaultScoreConfig.withHiddenScoreForReviewed,
        withScore: values?.withScore ?? defaultScoreConfig.withScore,
    };

    const scoreConfig: ScoreConfig = {
        coreFormQuestionWeights: {},
        formTemplateQuestionWeights: {},
        formWeight: valuesWithDefaults.formWeight,
        goalsWeight: valuesWithDefaults.goalsWeight,
        competenciesWeight: valuesWithDefaults.competenciesWeight,
        scaleEnd: valuesWithDefaults.scaleEnd,
        scaleStart: valuesWithDefaults.scaleStart,
        withGoalScore: valuesWithDefaults.withGoalScore,
        withCompetenciesScore: valuesWithDefaults.withCompetenciesScore,
        withHiddenScoreForReviewed: valuesWithDefaults.withHiddenScoreForReviewed,
        withScore: valuesWithDefaults.withScore,
        directionWeight: 0,
        qualitativeResultRanges: null,
        scoreDisplayType: ScoreDisplayType.NUMBER_ONLY,
    };

    const defaultFormReviewConfig = PerformanceReviewFormTemplate.getDefaultFormReviewConfig(valuesWithDefaults.evaluationType);
    const formReviewConfig: FormReviewConfig =
        values?.withCalibration !== undefined && isSubordinateReviewConfig(defaultFormReviewConfig)
            ? { ...defaultFormReviewConfig, withCalibration: values.withCalibration }
            : defaultFormReviewConfig;

    return new PerformanceReviewFormTemplate({
        coreFormId: fakePositiveInteger(),
        coreFormTag: faker.string.uuid(),
        createdAt: faker.date.recent(),
        cycleId,
        evaluationType: valuesWithDefaults.evaluationType,
        formReviewConfig,
        id: fakePositiveInteger(),
        scoreConfig,
        template: valuesWithDefaults.templateId ? generateRandomTemplate(valuesWithDefaults.templateId) : null,
        templateId: valuesWithDefaults.templateId,
        updatedAt: faker.date.recent(),
        ...formReviewConfig,
        ...scoreConfig,
    });
};

export const generatePerformanceReviewFormTemplateFromCreation = (formTemplate: PerformanceReviewFormTemplateForCreation) => {
    return new PerformanceReviewFormTemplate({
        coreFormId: formTemplate.coreFormId,
        coreFormTag: formTemplate.coreFormTag,
        createdAt: faker.date.recent(),
        cycleId: formTemplate.cycleId,
        evaluationType: formTemplate.evaluationType,
        formReviewConfig: formTemplate.formReviewConfig,
        id: fakePositiveInteger(),
        scoreConfig: formTemplate.scoreConfig,
        templateId: formTemplate.templateId,
        updatedAt: faker.date.recent(),
    });
};

export const generatePerformanceReviewTemplateFromCreation = (template: PerformanceReviewTemplateForCreation) => {
    return new PerformanceReviewTemplate({
        createdAt: faker.date.recent(),
        creatorId: template.creatorId,
        duplicationId: template.duplicationId,
        formTemplateId: template.formTemplateId,
        id: fakePositiveInteger(),
        instanceId: template.instanceId,
        goalsQuestionTemplateId: template.goalsQuestionTemplateId,
        competenciesQuestionTemplateId: template.competenciesQuestionTemplateId,
        questionsCount: template.questionsCount,
        status: template.status,
        title: template.title,
        updatedAt: faker.date.recent(),
    });
};

export const generateRandomPerformanceReviewFormTemplateWithTemplate = (
    cycleId: number,
    instanceId: number,
    values?: {
        evaluationType?: PerformanceReviewEvaluationTypesValues;
        formTemplateId?: number | null;
        withScore?: boolean;
    },
) => {
    const reviewFormTemplate = generatePerformanceReviewFormTemplateWithValues(cycleId, values);
    reviewFormTemplate.template = reviewFormTemplate.templateId !== null ? generateRandomTemplate(reviewFormTemplate.templateId, instanceId) : null;
    return reviewFormTemplate;
};

export const generatePerformanceReviewFormTemplateWithDraft = (
    cycleId: number,
    instanceId: number,
    values?: {
        evaluationType?: PerformanceReviewEvaluationTypesValues;
        formTemplateId?: number | null;
        withScore?: boolean;
    },
) => {
    const reviewFormTemplate = generateRandomPerformanceReviewFormTemplateWithTemplate(cycleId, instanceId, values);
    if (reviewFormTemplate.template?.formTemplate) {
        reviewFormTemplate.template.formTemplate.draft = generateFormDraftWithQuestions(reviewFormTemplate.template.formTemplate.draftId!);
    }
    return reviewFormTemplate;
};

export const generateQuestionWeightsFromIds = <T extends number | string>(ids: T[]): Record<T, number> => {
    if (ids.length === 0) return {} as Record<T, number>;
    let remainingWeight = 100;
    const questionWeights = ids.reduce((records, curr) => {
        records[curr] = fakePositiveInteger({ max: remainingWeight, min: 0 });
        remainingWeight = remainingWeight - records[curr];
        return records;
    }, {} as Record<T, number>);
    if (remainingWeight > 0) questionWeights[ids[0]] = questionWeights[ids[0]] + remainingWeight;
    return questionWeights;
};

export const generateRandomPerformanceReviewTargetUserForCreation = (cycleId: number) => {
    return new PerformanceReviewTargetUserForCreation({
        cycleId,
        responsibleId: faker.helpers.arrayElement([null, fakePositiveInteger()]),
        shareStatus: PerformanceReviewFormShareStatus.SHARE_DISABLED,
        userId: fakePositiveInteger(),
    });
};

export const generateRandomPerformanceReviewTargetUser = (
    cycleId: number,
    userId = fakePositiveInteger(),
    responsibleId: number | null = fakePositiveInteger(),
    shareStatus: PerformanceReviewFormShareStatusValues = PerformanceReviewFormShareStatus.SHARE_DISABLED,
    summary?: string,
) => {
    return new PerformanceReviewTargetUser({
        createdAt: faker.date.recent(),
        cycleId,
        id: fakePositiveInteger(),
        responsibleId,
        shareStatus,
        summary: summary ?? faker.lorem.paragraph(),
        updatedAt: faker.date.recent(),
        userId,
    });
};

export const getPerformanceReviewTargetUserFromCreation = (targetUserForCreation: PerformanceReviewTargetUserForCreation) => {
    return generateRandomPerformanceReviewTargetUser(
        targetUserForCreation.cycleId,
        targetUserForCreation.userId,
        targetUserForCreation.responsibleId,
        targetUserForCreation.shareStatus,
    );
};

export const generateRandomPerformanceReviewFormReviewForCreation = (cycleId: number) => {
    return new PerformanceReviewFormReviewForCreation({
        coreFormAnswerId: null,
        coreFormId: fakePositiveInteger(),
        coreFormTag: faker.string.uuid(),
        cycleId,
        duplicationId: faker.string.uuid(),
        externalReviewerId: fakePositiveInteger(),
        responsibleId: faker.helpers.arrayElement([null, fakePositiveInteger()]),
        reviewedId: fakePositiveInteger(),
        reviewerId: fakePositiveInteger(),
        shareStatus: PerformanceReviewFormShareStatus.SHARE_DISABLED,
        status: PerformanceReviewFormStatus.PENDING,
        type: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
    });
};

export const generateRandomPerformanceReviewFormReview = (cycleId: number) => {
    return new PerformanceReviewFormReview({
        coreFormAnswerId: null,
        coreFormId: fakePositiveInteger(),
        coreFormTag: faker.string.uuid(),
        createdAt: faker.date.recent(),
        cycleId,
        duplicationId: faker.string.uuid(),
        externalReviewerId: fakePositiveInteger(),
        id: fakePositiveInteger(),
        responsibleId: faker.helpers.arrayElement([null, fakePositiveInteger()]),
        reviewedId: fakePositiveInteger(),
        reviewerId: fakePositiveInteger(),
        score: null,
        shareStatus: PerformanceReviewFormShareStatus.SHARE_DISABLED,
        status: PerformanceReviewFormStatus.PENDING,
        type: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        updatedAt: faker.date.recent(),
    });
};

export const generateRandomNineBoxPoint = () => {
    const user = generateRandomPerformanceReviewUser(fakePositiveInteger());
    const formScore = fakePositiveInteger({ max: 100, min: 0 });
    const goalsScore = fakePositiveInteger({ max: 100, min: 0 });
    return new PerformanceReviewNineBoxPoint({
        formScore,
        goalsScore,
        user,
    });
};

export const generateRandomFormQuestion = (id: string, questionType = faker.helpers.enumValue(QUESTION_TYPES)): FormQuestionWithAnswer => {
    let choices;
    if (isChoiceQuestionType(questionType)) {
        choices = faker.helpers.multiple(faker.string.alpha);
    }
    const formQuestion: FormQuestionWithAnswer = {
        choices,
        commentableStatus: faker.helpers.arrayElement(Object.values(QUESTION_COMMENTABLE_STATUS)),
        description: faker.string.alpha(),
        id,
        referenceId: fakePositiveInteger(),
        required: faker.datatype.boolean(),
        title: faker.string.alpha(),
        type: questionType,
    };

    const formQuestionAnswer: QuestionAnswer = {
        answer: generateRandomOriginalAnswerForQuestion(questionType, { choices }),
        comment: faker.word.words(),
    };

    formQuestion.questionAnswer = formQuestionAnswer;
    return formQuestion;
};

export const generateGoalQuestionWithAnswer = (
    questionId?: string,
    goalId?: number,
    questionType = faker.helpers.enumValue(QUESTION_TYPES),
    progress?: string,
    weight?: number,
) => {
    const questionWithAnswer = generateRandomFormQuestion(questionId ?? faker.string.uuid(), questionType);
    return new PerformanceReviewGoalQuestionWithAnswer({
        createdAt: faker.date.recent(),
        cycleId: fakePositiveInteger(),
        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formReviewId: fakePositiveInteger(),
        goalId: goalId ?? fakePositiveInteger(),
        id: fakePositiveInteger(),
        progress: progress ?? fakePositiveInteger({ max: 100, min: 0 }).toString(),
        questionWithAnswer,
        updatedAt: faker.date.recent(),
        userId: fakePositiveInteger(),
        weight: weight ?? fakePositiveInteger({ max: 100, min: 0 }),
    });
};

export const generateCompetencyQuestionWithAnswer = (
    competencyId?: string,
    questionId?: string,
    evaluationType?: PerformanceReviewEvaluationTypesValues,
    formReviewId?: number,
    userId?: number,
    cycleId?: number,
    score?: number | null,
) => {
    const questionWithAnswer = generateRandomFormQuestion(questionId ?? faker.string.uuid());
    return new PerformanceReviewCompetencyQuestionWithAnswer({
        competencyId: competencyId ?? faker.string.uuid(),
        competency: null,
        createdAt: faker.date.recent(),
        cycleId: cycleId ?? fakePositiveInteger(),
        deletedAt: null,
        evaluationType: evaluationType ?? faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formReviewId: formReviewId ?? fakePositiveInteger(),
        id: faker.string.uuid(),
        questionWithAnswer,
        score: score ?? null,
        updatedAt: faker.date.recent(),
        userId: userId ?? fakePositiveInteger(),
    });
};

export const generateGoalQuestionWithAnswerAndGoal = (questionId?: string, goalId?: number, weight?: number) => {
    const goal = generateRandomGoal();
    const questionWithAnswer = generateRandomFormQuestion(questionId ?? faker.string.uuid());
    const goalQuestionsWithAnswer = new PerformanceReviewGoalQuestionWithAnswer({
        createdAt: faker.date.recent(),
        cycleId: fakePositiveInteger(),
        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formReviewId: fakePositiveInteger(),
        goalId: goalId ?? fakePositiveInteger(),
        id: fakePositiveInteger(),
        progress: goal.progress,
        questionWithAnswer,
        updatedAt: faker.date.recent(),
        userId: fakePositiveInteger(),
        weight: weight ?? fakePositiveInteger({ max: 100, min: 0 }),
    });
    return goalQuestionsWithAnswer;
};

export const generateGoalQuestionWithAnswerDAO = (goalQuestion: PerformanceReviewGoalQuestionWithAnswer) => {
    const dao = new PerformanceReviewGoalQuestionWithAnswerDAO();
    dao.id = goalQuestion.id;
    dao.updatedAt = goalQuestion.updatedAt;
    dao.createdAt = goalQuestion.createdAt;
    dao.deletedAt = goalQuestion.deletedAt;
    dao.cycleId = goalQuestion.cycleId;
    dao.goalId = goalQuestion.goalId;
    dao.userId = goalQuestion.userId;
    dao.questionWithAnswer = goalQuestion.questionWithAnswer;
    dao.evaluationType = goalQuestion.evaluationType;
    dao.formReviewId = goalQuestion.formReviewId;
    return dao;
};

export const generateGoalQuestionWithAnswerFromCreation = (goalQuestionWithAnswerForCreation: PerformanceReviewGoalQuestionWithAnswerForCreation) => {
    return new PerformanceReviewGoalQuestionWithAnswer({
        createdAt: faker.date.recent(),
        cycleId: goalQuestionWithAnswerForCreation.cycleId,
        evaluationType: goalQuestionWithAnswerForCreation.evaluationType,
        formReviewId: goalQuestionWithAnswerForCreation.formReviewId,
        goalId: goalQuestionWithAnswerForCreation.goalId,
        id: fakePositiveInteger(),
        progress: goalQuestionWithAnswerForCreation.progress,
        questionWithAnswer: goalQuestionWithAnswerForCreation.questionWithAnswer,
        updatedAt: faker.date.recent(),
        userId: goalQuestionWithAnswerForCreation.userId,
        weight: goalQuestionWithAnswerForCreation.weight,
    });
};

export const generateRandomGoalQuestionWithAnswerForCreation = (cycleId: number) => {
    return new PerformanceReviewGoalQuestionWithAnswerForCreation({
        cycleId,
        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        formReviewId: fakePositiveInteger(),
        goalId: fakePositiveInteger(),
        progress: fakePositiveInteger({ max: 100, min: 0 }).toString(),
        questionWithAnswer: generateRandomFormQuestion(faker.string.uuid()),
        userId: fakePositiveInteger(),
        weight: fakePositiveInteger({ max: 100, min: 0 }),
    });
};

export const generatePerformanceReviewFormReviewWithValues = (
    id: number,
    cycleId: number,
    values?: {
        externalReviewerId?: number | null;
        reviewerId?: number | null;
        reviewedId?: number;
        shareStatus?: PerformanceReviewFormShareStatusValues;
        status?: string;
        score?: number;
        goalsScore?: number;
        competenciesScore?: number;
        coreFormAnswerId?: number | null;
        responsibleId?: number;
        type?: PerformanceReviewEvaluationTypesValues;
    },
) => {
    const valuesWithDefaults = {
        coreFormAnswerId: values?.coreFormAnswerId !== undefined ? values?.coreFormAnswerId : null,
        externalReviewerId: values?.externalReviewerId ?? null,
        responsibleId: values?.responsibleId ?? faker.helpers.arrayElement([null, fakePositiveInteger()]),
        reviewedId: values?.reviewedId ?? fakePositiveInteger(),
        reviewerId: values?.reviewerId ?? null,
        shareStatus: values?.shareStatus ?? faker.helpers.arrayElement(Object.values(PerformanceReviewFormShareStatus)),
        status: values?.status ?? faker.helpers.arrayElement(Object.values(PerformanceReviewFormStatus)),
        type: values?.type ?? faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
    };
    const score = valuesWithDefaults.coreFormAnswerId === null ? null : values?.score ?? fakePositiveInteger();
    const goalsScore = valuesWithDefaults.coreFormAnswerId === null ? null : values?.goalsScore ?? fakePositiveInteger({ max: 100 });
    const competenciesScore = valuesWithDefaults.coreFormAnswerId === null ? null : values?.competenciesScore ?? fakePositiveInteger({ max: 100 });
    const formScore = valuesWithDefaults.coreFormAnswerId === null ? null : values?.goalsScore ?? fakePositiveInteger({ max: 100 });
    return new PerformanceReviewFormReview({
        coreFormAnswerId: valuesWithDefaults.coreFormAnswerId,
        coreFormId: fakePositiveInteger(),
        coreFormTag: faker.string.uuid(),
        competenciesScore,
        createdAt: faker.date.recent(),
        cycleId,
        duplicationId: faker.string.uuid(),
        externalReviewerId: valuesWithDefaults.externalReviewerId,
        formScore,
        goalsScore,
        id,
        responsibleId: valuesWithDefaults.responsibleId,
        reviewedId: valuesWithDefaults.reviewedId,
        reviewerId: valuesWithDefaults.reviewerId,
        score,
        shareStatus: valuesWithDefaults.shareStatus,
        status: valuesWithDefaults.status,
        type: valuesWithDefaults.type,
        updatedAt: faker.date.recent(),
    });
};

export const getPerformanceReviewFormReviewFromCreation = (formReviewForCreation: PerformanceReviewFormReviewForCreation) => {
    const creationDate = faker.date.recent();
    return new PerformanceReviewFormReview({
        coreFormAnswerId: null,
        coreFormId: formReviewForCreation.coreFormId,
        coreFormTag: formReviewForCreation.coreFormTag,
        createdAt: creationDate,
        cycleId: formReviewForCreation.cycleId,
        duplicationId: faker.string.uuid(),
        externalReviewerId: formReviewForCreation.externalReviewerId,
        id: fakePositiveInteger(),
        responsibleId: faker.helpers.arrayElement([null, fakePositiveInteger()]),
        reviewedId: formReviewForCreation.reviewedId,
        reviewerId: formReviewForCreation.reviewerId,
        shareStatus: formReviewForCreation.shareStatus,
        status: formReviewForCreation.status,
        type: formReviewForCreation.type,
        updatedAt: creationDate,
    });
};

export const getPerformanceReviewFormReviewFromDuplication = (formReviewDuplication: DuplicatePerformanceReviewFormReviewInterface) => {
    const review = getPerformanceReviewFormReviewFromCreation(formReviewDuplication);
    review.score = formReviewDuplication.score;
    review.formScore = formReviewDuplication.formScore;
    review.goalsScore = formReviewDuplication.goalsScore;
    review.competenciesScore = formReviewDuplication.competenciesScore;
    return review;
};

export const getPerformanceReviewFormTemplateFromDuplication = (formTemplateDuplication: DuplicatePerformanceReviewFormTemplateInterface) => {
    const formTemplate = generatePerformanceReviewFormTemplateFromCreation(formTemplateDuplication);
    formTemplate.scoreConfig = formTemplateDuplication.scoreConfig;
    formTemplate.formReviewConfig = formTemplateDuplication.formReviewConfig;
    return formTemplate;
};

export const generateRandomEditRequestForCreation = () => {
    return new PerformanceReviewFormReviewEditRequestForCreation({
        cycleId: fakePositiveInteger(),
        requestDescription: faker.word.words(),
        reviewFormId: fakePositiveInteger(),
        reviewType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        reviewedId: fakePositiveInteger(),
        reviewerId: fakePositiveInteger(),
        status: faker.helpers.arrayElement(Object.values(PerformanceReviewFormEditRequestStatus)),
    });
};

export const generateExternalReviewerFromCreation = (
    instanceId: number,
    externalReviewerForCreation: PerformanceReviewExternalReviewerForCreation,
) => {
    return new PerformanceReviewExternalReviewer({
        createdAt: faker.date.recent(),
        duplicationId: externalReviewerForCreation.duplicationId,
        email: externalReviewerForCreation.email,
        firstName: externalReviewerForCreation.firstName,
        id: fakePositiveInteger(),
        instanceId,
        lastName: externalReviewerForCreation.lastName,
        updatedAt: faker.date.recent(),
    });
};

export const generateRandomExternalReviewer = (values?: { id?: number; instanceId?: number; email?: string }) => {
    return new PerformanceReviewExternalReviewer({
        createdAt: faker.date.recent(),
        duplicationId: faker.string.uuid(),
        email: values?.email ?? faker.word.words(),
        firstName: faker.word.words(),
        id: values?.id ?? fakePositiveInteger(),
        instanceId: values?.instanceId ?? fakePositiveInteger(),
        lastName: faker.word.words(),
        updatedAt: faker.date.recent(),
    });
};

export const generateRandomExternalReviewerForCreation = () => {
    return new PerformanceReviewExternalReviewerForCreation({
        duplicationId: faker.string.uuid(),
        email: faker.word.words(),
        firstName: faker.word.words(),
        instanceId: fakePositiveInteger(),
        lastName: faker.word.words(),
    });
};

export const createExternalReviewerDAO = (externalReviewer: PerformanceReviewExternalReviewer) => {
    const dao = new PerformanceReviewExternalReviewerDAO();
    dao.id = externalReviewer.id;
    dao.createdAt = externalReviewer.createdAt;
    dao.updatedAt = externalReviewer.updatedAt;
    dao.deletedAt = externalReviewer.deletedAt;
    dao.duplicationId = externalReviewer.duplicationId;
    dao.instanceId = externalReviewer.instanceId;
    dao.email = externalReviewer.email;
    dao.firstName = externalReviewer.firstName;
    dao.lastName = externalReviewer.lastName;
    return dao;
};

export const generateRandomEditRequest = (values?: {
    id?: number;
    status?: string;
    reviewType?: string;
    reviewedId?: number;
    reviewerId?: number;
    cycleId?: number;
    reviewFormId?: number;
}) => {
    return new PerformanceReviewFormReviewEditRequest({
        createdAt: faker.date.recent(),
        cycleId: values?.cycleId ?? fakePositiveInteger(),
        id: values?.id ?? fakePositiveInteger(),
        requestDescription: faker.word.words(),
        resolutionDescription: faker.word.words(),
        reviewFormId: values?.reviewFormId ?? fakePositiveInteger(),
        reviewType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
        reviewedId: fakePositiveInteger(),
        reviewerId: values?.reviewerId ?? fakePositiveInteger(),
        status: values?.status ?? faker.helpers.arrayElement(Object.values(PerformanceReviewFormEditRequestStatus)),
        updatedAt: faker.date.recent(),
    });
};

export const createEditRequestDAO = (editRequest: PerformanceReviewFormReviewEditRequest) => {
    const dao = new PerformanceReviewFormReviewEditRequestDAO();
    dao.id = editRequest.id;
    dao.createdAt = editRequest.createdAt;
    dao.updatedAt = editRequest.updatedAt;
    dao.deletedAt = editRequest.deletedAt;
    dao.cycleId = editRequest.cycleId;
    dao.status = editRequest.status;
    dao.reviewType = editRequest.reviewType;
    dao.reviewedId = editRequest.reviewedId;
    dao.reviewerId = editRequest.reviewerId;
    dao.requestDescription = editRequest.requestDescription;
    dao.resolutionDescription = editRequest.resolutionDescription;
    dao.reviewFormId = editRequest.reviewFormId;
    return dao;
};

export const generateRandomTemplateForCreation = () => {
    return new PerformanceReviewTemplateForCreation({
        creatorId: fakePositiveInteger(),
        duplicationId: faker.string.uuid(),
        formTemplateId: fakePositiveInteger(),
        instanceId: fakePositiveInteger(),
        goalsQuestionTemplateId: fakePositiveInteger(),
        competenciesQuestionTemplateId: fakePositiveInteger(),
        questionsCount: fakePositiveInteger(),
        status: faker.helpers.arrayElement(Object.values(FORM_TEMPLATE_STATUS)),
        title: faker.word.words(),
    });
};

export const generateRandomTemplate = (
    templateId?: number,
    instanceId?: number,
    goalsProgressMode?: PerformanceReviewTemplateGoalsProgressModeValues,
    withCompetencies?: boolean,
) => {
    const template = new PerformanceReviewTemplate({
        createdAt: faker.date.recent(),
        creatorId: fakePositiveInteger(),
        duplicationId: faker.string.uuid(),
        formTemplateId: fakePositiveInteger(),
        goalsProgressMode: goalsProgressMode ?? null,
        id: templateId ?? fakePositiveInteger(),
        instanceId: instanceId ?? fakePositiveInteger(),
        goalsQuestionTemplateId: goalsProgressMode === PerformanceReviewTemplateGoalsProgressMode.MANUAL ? fakePositiveInteger() : null,
        competenciesQuestionTemplateId: withCompetencies ? fakePositiveInteger() : null,
        questionsCount: fakePositiveInteger(),
        status: faker.helpers.arrayElement(Object.values(FORM_TEMPLATE_STATUS)),
        title: faker.word.words(),
        updatedAt: faker.date.recent(),
    });
    template.formTemplate = generateFormTemplateWithValues(template.formTemplateId!, template.instanceId);
    if(goalsProgressMode === PerformanceReviewTemplateGoalsProgressMode.MANUAL)
        template.goalsQuestionTemplate = generateQuestionTemplateWithValues(template.goalsQuestionTemplateId!, template.instanceId);
    if(withCompetencies)
        template.competenciesQuestionTemplate = generateQuestionTemplateWithValues(template.competenciesQuestionTemplateId!, template.instanceId);
    return template;
};

export const generateRandomPerformanceReviewUser = (userId: number) => {
    return new PerformanceReviewUser({
        deleted: faker.datatype.boolean(),
        employeeInternalId: faker.internet.email(),
        firstName: faker.person.firstName(),
        lastName: faker.person.lastName(),
        profilePicture: faker.internet.url(),
        userId,
        userStatus: faker.helpers.arrayElement([UserStatus.ACTIVE, UserStatus.UNCLAIMED, UserStatus.DEACTIVATED]),
    });
};

export const generateRandomPerformanceReviewUserStats = (): PerformanceReviewUserStats => {
    const totalReviews = fakePositiveInteger();
    const filledReviews = faker.number.int({ max: totalReviews });
    const status =
        filledReviews === totalReviews
            ? PerformanceReviewUserStatus.FINISHED
            : filledReviews === 0
            ? PerformanceReviewUserStatus.PENDING
            : PerformanceReviewUserStatus.IN_PROGRESS;
    const score = filledReviews === 0 ? null : fakePositiveInteger();
    return {
        filledReviews,
        pendingReviews: totalReviews - filledReviews,
        status,
        totalReviews,
    };
};

export const addStatsToPerformanceReviewUser = (reviewUser: PerformanceReviewUser): PerformanceReviewUserWithStats => {
    const userStats = generateRandomPerformanceReviewUserStats();
    return new PerformanceReviewUserWithStats({
        deleted: reviewUser.deleted,
        employeeInternalId: reviewUser.employeeInternalId,
        filledReviews: userStats.filledReviews,
        firstName: reviewUser.firstName,
        lastName: reviewUser.lastName,
        pendingReviews: userStats.pendingReviews,
        profilePicture: reviewUser.profilePicture,
        score: null,
        status: userStats.status,
        totalReviews: userStats.totalReviews,
        userId: reviewUser.userId,
    });
};

export const generateFormTemplateWithValues = (
    id: number,
    instanceId: number,
    values?: {
        title?: string;
        description?: string;
        origin?: string;
        creatorId?: number;
        formTemplateId?: number;
        status?: FormTemplateStatusValues;
        questionTemplates: QuestionTemplate[];
        questionCount?: number;
    },
) => {
    const valuesWithDefaults = {
        createdAt: faker.date.recent(),
        creatorId: values?.creatorId ?? fakePositiveInteger(),
        deletedAt: null,
        description: values?.description ?? faker.lorem.paragraph(),
        draftId: faker.string.uuid(),
        formTemplateId: values?.formTemplateId ?? fakePositiveInteger(),
        id: id ?? fakePositiveInteger(),
        instanceId: instanceId ?? fakePositiveInteger(),
        origin: values?.origin ?? faker.helpers.arrayElement(Object.values(FORM_TEMPLATE_ORIGINS)),
        questionCount: values?.questionCount ?? fakePositiveInteger(),
        status: values?.status ?? faker.helpers.arrayElement(Object.values(FORM_TEMPLATE_STATUS)),
        title: values?.title ?? faker.word.words(),
        updatedAt: faker.date.recent(),
    };
    return new FormTemplate(
        valuesWithDefaults.creatorId,
        instanceId,
        valuesWithDefaults.title,
        valuesWithDefaults.origin,
        valuesWithDefaults.questionCount,
        valuesWithDefaults.description,
        valuesWithDefaults.draftId,
        valuesWithDefaults.status,
        valuesWithDefaults.createdAt,
        valuesWithDefaults.updatedAt,
        valuesWithDefaults.deletedAt,
        id,
    );
};

export const generateRandomUser = () => {
    return new User()
        .setId(fakePositiveInteger())
        .setInstanceId(fakePositiveInteger())
        .setFirstName(faker.person.firstName())
        .setLastName(faker.person.lastName())
        .setEmployeeInternalId(faker.internet.email())
        .setProfilePicture(faker.helpers.arrayElement([null, faker.internet.url()]));
};

export const generateRandomReviewCountsForTypesAndStatuses = (
    types: PerformanceReviewEvaluationTypesValues[],
    statuses: string[],
): ReviewStatsByTypeAndStatus => {
    return types.reduce((typeRecords, type) => {
        typeRecords[type] = statuses.reduce((statusRecords, status) => {
            statusRecords[status] = fakePositiveInteger();
            return statusRecords;
        }, {} as { [key in PerformanceReviewFormStatusValues]: number });
        return typeRecords;
    }, {} as ReviewStatsByTypeAndStatus);
};

export const generateRandomReviewUserStatsByType = (types: PerformanceReviewEvaluationTypesValues[]): UserStatsByType => {
    return types.reduce((typeRecords, type) => {
        typeRecords[type] = {
            filledReviews: fakePositiveInteger(),
            status: faker.helpers.arrayElement(Object.values(PerformanceReviewUserStatus)),
            totalReviews: fakePositiveInteger(),
            score: fakePositiveInteger(),
        };
        return typeRecords;
    }, {} as UserStatsByType);
};

export const generateRandomReviewCountsForShareStatuses = (statuses: string[]): ReviewStatsByShareStatus => {
    return statuses.reduce((statusRecords, status) => {
        statusRecords[status] = fakePositiveInteger();
        return statusRecords;
    }, {} as ReviewStatsByShareStatus);
};

export const createPerformanceReviewCycleDAO = (reviewCycle: PerformanceReviewCycle, reviewFormTemplates: PerformanceReviewFormTemplate[] = []) => {
    const dao = new PerformanceReviewCycleDAO();
    dao.id = reviewCycle.id;
    dao.createdAt = reviewCycle.createdAt;
    dao.updatedAt = reviewCycle.updatedAt;
    dao.instanceId = reviewCycle.instanceId;
    dao.name = reviewCycle.name;
    dao.startDate = reviewCycle.startDate;
    dao.endDate = reviewCycle.endDate;
    dao.status = reviewCycle.status;
    dao.reviewedUsersMode = reviewCycle.reviewedUsersMode;
    dao.formTemplates = reviewFormTemplates.map((reviewFormTemplate) => createPerformanceReviewFormTemplateDAO(reviewFormTemplate));
    return dao;
};

export const createPerformanceReviewTemplateDAO = (reviewTemplate: PerformanceReviewTemplate, goalsQuestionTemplate?: QuestionTemplate, competenciesQuestionTemplate?: QuestionTemplate) => {
    const dao = new PerformanceReviewTemplateDAO();
    dao.id = reviewTemplate.id;
    dao.instanceId = reviewTemplate.instanceId;
    dao.createdAt = reviewTemplate.createdAt;
    dao.updatedAt = reviewTemplate.updatedAt;
    dao.formTemplateId = reviewTemplate.formTemplateId;
    dao.title = reviewTemplate.title;
    dao.status = reviewTemplate.status;
    dao.questionsCount = reviewTemplate.questionsCount;
    dao.questionTemplateId = reviewTemplate.goalsQuestionTemplateId;
    dao.formTemplate = createFormTemplateDAO(reviewTemplate.formTemplateId!, reviewTemplate.instanceId);
    dao.questionTemplate = goalsQuestionTemplate ? createQuestionTemplateDAO(goalsQuestionTemplate) : undefined; // TODO: SQPM-4926 Remove this line
    dao.goalsQuestionTemplate = goalsQuestionTemplate ? createQuestionTemplateDAO(goalsQuestionTemplate) : undefined;
    dao.competenciesQuestionTemplate = competenciesQuestionTemplate ? createQuestionTemplateDAO(competenciesQuestionTemplate) : undefined;
    return dao;
};

export const createPerformanceReviewFormTemplateDAO = (reviewFormTemplate: PerformanceReviewFormTemplate) => {
    const dao = new PerformanceReviewFormTemplateDAO();
    dao.id = reviewFormTemplate.id;
    dao.createdAt = reviewFormTemplate.createdAt;
    dao.updatedAt = reviewFormTemplate.updatedAt;
    dao.cycleId = reviewFormTemplate.cycleId;
    dao.evaluationType = reviewFormTemplate.evaluationType;
    dao.templateId = reviewFormTemplate.templateId;
    dao.coreFormId = reviewFormTemplate.coreFormId;
    dao.coreFormTag = reviewFormTemplate.coreFormTag;
    dao.scoreConfig = reviewFormTemplate.scoreConfig;
    dao.formReviewConfig = reviewFormTemplate.formReviewConfig;
    return dao;
};

export const createPerformanceReviewTargetUserDAO = (targetUser: PerformanceReviewTargetUser) => {
    const dao = new PerformanceReviewTargetUserDAO();
    dao.id = targetUser.id;
    dao.createdAt = targetUser.createdAt;
    dao.updatedAt = targetUser.updatedAt;
    dao.deletedAt = targetUser.deletedAt;
    dao.cycleId = targetUser.cycleId;
    dao.userId = targetUser.userId;
    dao.responsibleId = targetUser.responsibleId;
    dao.shareStatus = targetUser.shareStatus;
    return dao;
};

export const createPerformanceReviewFormReviewDAO = (formReview: PerformanceReviewFormReview) => {
    const dao = new PerformanceReviewFormReviewDAO();
    dao.id = formReview.id;
    dao.createdAt = formReview.createdAt;
    dao.updatedAt = formReview.updatedAt;
    dao.deletedAt = formReview.deletedAt;
    dao.cycleId = formReview.cycleId;
    dao.type = formReview.type;
    dao.status = formReview.status;
    dao.shareStatus = formReview.shareStatus;
    dao.responsibleId = formReview.responsibleId;
    dao.externalReviewerId = formReview.externalReviewerId;
    dao.reviewerId = formReview.reviewerId;
    dao.reviewedId = formReview.reviewedId;
    dao.coreFormId = formReview.coreFormId;
    dao.coreFormTag = formReview.coreFormTag;
    dao.coreFormAnswerId = formReview.coreFormAnswerId;
    dao.score = formReview.score;
    dao.goalsScore = formReview.goalsScore;
    dao.competenciesScore = formReview.competenciesScore;
    dao.formScore = formReview.formScore;
    return dao;
};

export const createPerformanceReviewUserDAO = (reviewUser: PerformanceReviewUser) => {
    const dao = new PerformanceReviewUserDAO();
    dao.userId = reviewUser.userId;
    dao.createdAt = faker.date.recent();
    dao.updatedAt = faker.date.recent();
    dao.firstName = reviewUser.firstName;
    dao.lastName = reviewUser.lastName;
    dao.profilePicture = reviewUser.profilePicture;
    dao.deleted = reviewUser.deleted;
    dao.employeeInternalId = reviewUser.employeeInternalId;
    return dao;
};

export const addStatsToPerformanceReviewUserDAO = (reviewUserDAO: PerformanceReviewUserDAO, amountOfUsers: number) => {
    const totalReviews = fakePositiveInteger();
    const filledReviews = fakePositiveInteger({ max: totalReviews, min: 0 });
    const status =
        filledReviews === totalReviews
            ? PerformanceReviewUserStatus.FINISHED
            : filledReviews === 0
            ? PerformanceReviewUserStatus.PENDING
            : PerformanceReviewUserStatus.IN_PROGRESS;
    return {
        ...reviewUserDAO,
        amountOfUsers,
        filledReviews,
        pendingReviews: totalReviews - filledReviews,
        status,
        totalReviews,
    };
};

const checkFormTemplateSerialized = (model: FormTemplate, dto: FormTemplateDTO) => {
    expect(dto.id).toBe(model.id);
    expect(dto.title).toBe(model.title);
    expect(dto.description).toBe(model.description);
    expect(dto.status).toBe(model.status);
};

const checkQuestionTemplateSerialized = (model: QuestionTemplate, dto: QuestionTemplateDTO) => {
    expect(dto.id).toBe(model.id);
    expect(dto.title).toBe(model.title);
    expect(dto.description).toBe(model.description);
    expect(dto.status).toBe(model.status);
    expect(dto.type).toBe(model.type);
};

export const checkPerformanceReviewFormTemplateSerialized = (model: PerformanceReviewFormTemplate, dto: PerformanceReviewFormTemplateDTO) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.cycleId).toBe(model.cycleId);
    expect(dto.evaluationType).toBe(model.evaluationType);
    expect(dto.templateId).toBe(model.templateId);
    expect(dto.id).toBe(model.id);
    expect(dto.updatedAt).toBe(model.updatedAt);
    expect(dto.withGoalScore).toBe(model.scoreConfig?.withGoalScore ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().withGoalScore);
    expect(dto.withCompetenciesScore).toBe(model.scoreConfig?.withCompetenciesScore ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().withCompetenciesScore);
    expect(dto.withScore).toBe(model.scoreConfig?.withScore ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().withScore);
    expect(dto.withCalibration).toBe(isSubordinateReviewConfig(model.formReviewConfig) ? model.formReviewConfig.withCalibration : null);
    if (!model.template) expect(dto.template).toBeNull();
    else {
        expect(dto.template).not.toBeNull();
        checkTemplateSerialized(model.template, dto.template as PerformanceReviewTemplateDTO);
    }
};

export const checkPerformanceReviewFormTemplateWithScoringInfoSerialized = (
    model: PerformanceReviewFormTemplate,
    dto: PerformanceReviewFormTemplateWithScoringInfoDTO,
) => {
    checkPerformanceReviewFormTemplateSerialized(model, dto);
    expect(dto.formQuestionWeights).toHaveLength(Object.keys(model.scoreConfig?.formTemplateQuestionWeights ?? {}).length);
    expect(dto.scaleEnd).toBe(model.scoreConfig?.scaleEnd ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().scaleEnd);
    expect(dto.scaleStart).toBe(model.scoreConfig?.scaleStart ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().scaleStart);
    expect(dto.formWeight).toBe(model.scoreConfig?.formWeight ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().formWeight);
    expect(dto.goalsWeight).toBe(model.scoreConfig?.goalsWeight ?? PerformanceReviewFormTemplate.getDefaultScoreConfig().goalsWeight);
    dto.formQuestionWeights.forEach((dtoQuestionWeight) => {
        const questionWeight = model.scoreConfig?.formTemplateQuestionWeights[dtoQuestionWeight.referenceId];
        expect(questionWeight).toBeDefined();
        expect(dtoQuestionWeight.weight).toBe(questionWeight);
    });
    if (!model.ratingQuestionTemplates) expect(dto.ratingQuestionTemplates).toBeNull();
    else {
        expect(dto.ratingQuestionTemplates).not.toBeNull();
        dto.ratingQuestionTemplates?.forEach((questionTemplateDto, index) => {
            checkQuestionTemplateSerialized(model.ratingQuestionTemplates![index], questionTemplateDto);
        });
    }
};

export const checkPerformanceReviewUserSerialized = (model: PerformanceReviewUser, dto: PerformanceReviewUserDTO) => {
    expect(dto.employeeInternalId).toBe(model.employeeInternalId);
    expect(dto.firstName).toBe(model.firstName);
    expect(dto.lastName).toBe(model.lastName);
    expect(dto.profilePicture).toBe(model.profilePicture);
    expect(dto.userId).toBe(model.userId);
    expect(dto.deleted).toBe(model.deleted);
};

export const checkPerformanceReviewUserWithStatsSerialized = (model: PerformanceReviewUserWithStats, dto: PerformanceReviewUserWithStatsDTO) => {
    checkPerformanceReviewUserSerialized(model, dto);
    expect(dto.filledReviews).toBe(model.filledReviews);
    expect(dto.pendingReviews).toBe(model.pendingReviews);
    expect(dto.status).toBe(model.status);
    expect(dto.totalReviews).toBe(model.totalReviews);
    expect(dto.score).toBe(model.score);
    expect(dto.statsPerEvaluationType).toBe(model.statsPerEvaluationType);
    expect(dto.shareStatus).toBe(model.shareStatus);
    expect(dto.teamScoreDiff).toBe(model.teamScoreDiff);
};

export const checkPerformanceReviewTargetUserSerialized = (
    model: PerformanceReviewTargetUser,
    dto: PerformanceReviewTargetUserDTO,
    scoreNeedsObfuscation?: boolean,
) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.cycleId).toBe(model.cycleId);
    expect(dto.id).toBe(model.id);
    expect(dto.responsibleId).toBe(model.responsibleId);
    expect(dto.shareStatus).toBe(model.shareStatus);
    expect(dto.updatedAt).toBe(model.updatedAt);
    expect(dto.userId).toBe(model.userId);
    expect(dto.summary).toBe(model.summary);
    if (!model.responsible) expect(dto.responsible).toBeNull();
    else {
        expect(dto.responsible).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.responsible, dto.responsible as PerformanceReviewUserDTO);
    }
    if (!model.user) expect(dto.user).toBeNull();
    else {
        expect(dto.user).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.user, dto.user as PerformanceReviewUserDTO);
    }
    if (!model.reviewedFormReviews) expect(dto.score).toBeNull();
    else {
        if (scoreNeedsObfuscation) expect(dto.score).toBeNull();
        else {
            expect(dto.score).toBe(model.score);
        }
    }
};

export const checkPerformanceReviewFormReviewSerialized = (
    model: PerformanceReviewFormReview,
    dto: PerformanceReviewFormReviewDTO,
    scoreNeedsObfuscation?: boolean,
) => {
    expect(dto.answered).toBe(!!model.coreFormAnswerId);
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.cycleId).toBe(model.cycleId);
    expect(dto.id).toBe(model.id);
    expect(dto.responsibleId).toBe(model.responsibleId);
    expect(dto.reviewedId).toBe(model.reviewedId);
    expect(dto.reviewerId).toBe(model.reviewerId);
    expect(dto.externalReviewerId).toBe(model.externalReviewerId);
    expect(dto.shareStatus).toBe(model.shareStatus);
    if (scoreNeedsObfuscation) expect(dto.score).toBeNull();
    else expect(dto.score).toBe(model.score);
    expect(dto.status).toBe(model.status);
    expect(dto.type).toBe(model.type);
    expect(dto.updatedAt).toBe(model.updatedAt);
    if (!model.reviewed) expect(dto.reviewed).toBeNull();
    else {
        expect(dto.reviewed).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.reviewed, dto.reviewed as PerformanceReviewUserDTO);
    }
    if (!model.reviewer) expect(dto.reviewer).toBeNull();
    else {
        expect(dto.reviewer).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.reviewer, dto.reviewer as PerformanceReviewUserDTO);
    }
    if (!model.externalReviewer) expect(dto.externalReviewer).toBeNull();
    else {
        expect(dto.externalReviewer).not.toBeNull();
        checkPerformanceReviewExternalReviewerSerialized(model.externalReviewer, dto.externalReviewer as PerformanceReviewExternalReviewerDAO);
    }
};

export const checkNineBoxPointSerialized = (model: PerformanceReviewNineBoxPoint, dto: PerformanceReviewNineBoxPointDTO) => {
    expect(dto.formScore).toBe(model.formScore);
    expect(dto.goalsScore).toBe(model.goalsScore);
    checkPerformanceReviewUserSerialized(model.user, dto.user as PerformanceReviewUserDTO);
};

export const checkPerformanceReviewFormReviewSerializedWithCoreForm = (
    model: PerformanceReviewFormReview,
    dto: PerformanceReviewFormReviewWithFormDetailsDTO,
) => {
    checkPerformanceReviewFormReviewSerialized(model, dto);
    if (!model.coreForm) expect(dto.form).toBeNull();
    else {
        expect(dto.form).not.toBeNull();
        checkPerformanceReviewCoreFormSerialized(
            model.coreForm,
            model.goalQuestionsWithAnswer,
            model.competencyQuestionsWithAnswer,
            dto.form as PerformanceReviewCoreFormContentDTO,
            model.formAnswer,
        );
    }
};

export const checkPerformanceReviewGoalQuestionSerialized = (
    model: PerformanceReviewGoalQuestionWithAnswerDataFields,
    dto: PerformanceReviewGoalQuestionWithAnswerDTO,
) => {
    const hasQuestionWithAnswer = 'choices' in dto && dto.choices !== null && model.questionWithAnswer !== null;
    if (hasQuestionWithAnswer) {
        expect(dto.choices).toStrictEqual(model.questionWithAnswer!.choices ?? null);
        expect(dto.commentableStatus).toBe(model.questionWithAnswer!.commentableStatus);
        expect(dto.defaultCountryCode).toBe(model.questionWithAnswer!.defaultCountryCode);
        expect(dto.description).toBe(model.questionWithAnswer!.description);
        expect(dto.id).toBe(model.questionWithAnswer!.id);
        expect(dto.pattern).toBe(model.questionWithAnswer!.pattern);
        expect(dto.required).toBe(model.questionWithAnswer!.required);
        expect(dto.rows).toStrictEqual(model.questionWithAnswer!.rows);
        expect(dto.title).toBe(model.questionWithAnswer!.title);
        expect(dto.type).toBe(model.questionWithAnswer!.type);
        expect(dto.userField).toBe(model.questionWithAnswer!.userField);

        const questionAnswer = model.questionWithAnswer!.questionAnswer;
        if (questionAnswer) {
            expect(dto.answer).toBe(questionAnswer.answer);
            expect(dto.comment).toBe(questionAnswer.comment);
        } else {
            expect(dto.answer).toBeNull();
            expect(dto.comment).toBeNull();
        }
    }
    if (!model.goal) expect(dto.goal).toBeNull();
    else {
        expect(dto.goal).not.toBeNull();
        checkGoalSerialized(model.goal, dto.goal as GoalDTO);
        expect(dto.goal?.weight).toBe(model.weight);
    }
};

export const checkPerformanceReviewCompetencyQuestionSerialized = (
    model: PerformanceReviewCompetencyQuestionWithAnswerDataFields,
    dto: PerformanceReviewCompetencyQuestionWithAnswerDTO,
) => {
    const hasQuestionWithAnswer = 'choices' in dto && dto.choices !== null && model.questionWithAnswer !== null;
    if (hasQuestionWithAnswer) {
        const questionAnswers = new Map<string, QuestionAnswer>();
        questionAnswers.set(model.questionWithAnswer!.id, model.questionWithAnswer!.questionAnswer!);
        checkPerformanceReviewCoreFormQuestionSerialized(model.questionWithAnswer!, dto, questionAnswers);
    }
    if (!model.competency) expect(dto.competency).toBeNull();
    else {
        expect(dto.competency).not.toBeNull();
        checkCompetencySerialized(model.competency, dto.competency as CompetencyDTO);
    }
};

export const checkPerformanceReviewCoreFormSerialized = (
    model: CoreForm,
    goalQuestionsWithAnswer: PerformanceReviewGoalQuestionWithAnswerDataFields[] | null,
    competencyQuestionsWithAnswer: PerformanceReviewCompetencyQuestionWithAnswerDataFields[] | null,
    dto: PerformanceReviewCoreFormContentDTO,
    formAnswer?: FormAnswer | null,
) => {
    expect(dto.title).toBe(model.content.title);
    expect(dto.description).toBe(model.content.description);
    if (formAnswer) {
        expect(dto.withAnswer).toBe(true);
        expect(dto.answeredAt).not.toBeNull();
    } else {
        expect(dto.withAnswer).toBe(false);
    }

    const questionAnswers = formAnswer?.getConvertedQuestionAnswersById() ?? new Map<string, QuestionAnswer>();
    const questions = model.content.steps.flatMap((step) => step.questions);
    const goalQuestionsLength = goalQuestionsWithAnswer?.length ?? 0;
    expect(dto.questions).toHaveLength(questions.length + goalQuestionsLength);
    let goalQuestionWithAnswerIndex = 0;
    let competencyQuestionWithAnswerIndex = 0;
    dto.questions.forEach((questionDto, index) => {
        if (isGoalQuestionWithAnswer(questionDto)) {
            checkPerformanceReviewGoalQuestionSerialized(goalQuestionsWithAnswer![goalQuestionWithAnswerIndex++], questionDto);
        } 
        if (isCompetencyQuestionWithAnswer(questionDto)) {
            checkPerformanceReviewCompetencyQuestionSerialized(competencyQuestionsWithAnswer![competencyQuestionWithAnswerIndex++], questionDto);
        } 
        if (isPerformanceReviewCoreFormQuestion(questionDto)) {
            checkPerformanceReviewCoreFormQuestionSerialized(questions[index], questionDto, questionAnswers);
        }
        
    });
};

const checkPerformanceReviewCoreFormQuestionSerialized = (
    model: CoreFormQuestion,
    dto: PerformanceReviewCoreFormQuestionDTO,
    questionAnswers: Map<string, QuestionAnswer>,
) => {
    expect(dto.choices).toStrictEqual(model.choices ?? null);
    expect(dto.commentableStatus).toBe(model.commentableStatus);
    expect(dto.defaultCountryCode).toBe(model.defaultCountryCode);
    expect(dto.description).toBe(model.description);
    expect(dto.id).toBe(model.id);
    expect(dto.pattern).toBe(model.pattern);
    expect(dto.required).toBe(model.required);
    expect(dto.rows).toStrictEqual(model.rows);
    expect(dto.title).toBe(model.title);
    expect(dto.type).toBe(model.type);
    expect(dto.userField).toBe(model.userField);

    const questionAnswer = questionAnswers.get(model.id);
    if (questionAnswer) {
        expect(dto.answer).toBe(questionAnswer.answer);
        expect(dto.comment).toBe(questionAnswer.comment);
    } else {
        expect(dto.answer).toBeNull();
        expect(dto.comment).toBeNull();
    }
};

export const checkPerformanceReviewCycleSerialized = (
    model: PerformanceReviewCycle,
    dto: PerformanceReviewCycleDTO,
    scoreNeedsObfuscation?: boolean,
) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.endDate).toBe(model.endDate);
    expect(dto.id).toBe(model.id);
    expect(dto.name).toBe(model.name);
    expect(dto.peerReviewExternalReviewerSelectionVisibleForBosses).toBe(model.peerReviewExternalReviewerSelectionVisibleForBosses);
    expect(dto.peerReviewReviewerSelectionVisibleForBosses).toBe(model.peerReviewReviewerSelectionVisibleForBosses);
    expect(dto.reviewedUsersMode).toBe(model.reviewedUsersMode);
    expect(dto.reviewerLimitByHiringDate).toBe(model.reviewerLimitByHiringDate);
    expect(dto.reviewsVisibleAfterFinishedReviewCycle).toBe(model.reviewsVisibleAfterFinishedReviewCycle);
    expect(dto.canBeShared).toBe(model.canBeShared);
    expect(dto.startDate).toBe(model.startDate);
    expect(dto.status).toBe(model.status);
    expect(dto.updatedAt).toBe(model.updatedAt);
    expect(dto.needsChanges).toBe(model.needsChanges);
    expect(dto.isResponsible).toBe(model.isResponsible);
    expect(dto.withSummary).toBe(model.withSummary);
    expect(dto.upwardReviewAnonymousAnswers).toBe(model.upwardReviewAnonymousAnswers);
    expect(dto.hasPendingReviews).toBe(model.hasPendingReviews);
    expect(dto.goalCycleId).toBe(model.goalCycleId);

    if (!model.formTemplates) expect(dto.formTemplates).toBeNull();
    else {
        const formTemplates = model.formTemplates;

        expect(dto.formTemplates).not.toBeNull();
        expect(dto.formTemplates).toHaveLength(formTemplates.length);
        (dto.formTemplates as PerformanceReviewFormTemplateDTO[]).forEach((formTemplateDTO, index) => {
            checkPerformanceReviewFormTemplateSerialized(formTemplates[index], formTemplateDTO);
        });
    }
    if (!model.targetUsers) expect(dto.targetUserIds).toBeNull();
    else {
        const targetUsers = model.targetUsers;

        expect(dto.targetUserIds).not.toBeNull();
        expect(dto.targetUserIds).toHaveLength(targetUsers.length);
        (dto.targetUserIds as number[]).forEach((userId, index) => {
            expect(userId).toBe(targetUsers[index].userId);
        });
    }
    if (!model.userTargetUser) expect(dto.userTargetUser).toBeNull();
    else {
        expect(dto.userTargetUser).not.toBeNull();
        checkPerformanceReviewTargetUserSerialized(model.userTargetUser, dto.userTargetUser as PerformanceReviewTargetUserDTO, scoreNeedsObfuscation);
    }
};

export const checkPerformanceReviewExternalReviewerSerialized = (
    model: PerformanceReviewExternalReviewer,
    dto: PerformanceReviewExternalReviewerDTO,
) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.email).toBe(model.email);
    expect(dto.id).toBe(model.id);
    expect(dto.firstName).toBe(model.firstName);
    expect(dto.lastName).toBe(model.lastName);
    expect(dto.updatedAt).toBe(model.updatedAt);
};

const checkModelReviewsSerialized = (
    modelReviews: PerformanceReviewFormReview[] | null,
    dtoReviews: PerformanceReviewFormReviewDTO[] | null,
    scoreNeedsObfuscation?: boolean,
) => {
    if (!modelReviews) expect(dtoReviews).toBeNull();
    else {
        expect(dtoReviews).not.toBeNull();
        expect(dtoReviews).toHaveLength(modelReviews.length);
        (dtoReviews as PerformanceReviewFormReviewDTO[]).forEach((formReviewDTO, index) => {
            checkPerformanceReviewFormReviewSerialized(modelReviews[index], formReviewDTO, scoreNeedsObfuscation);
        });
    }
};

export const checkPerformanceReviewTargetUserSerializedWithUserReviews = (
    model: PerformanceReviewTargetUser,
    dto: PerformanceReviewTargetUserWithUserReviewsDTO,
    scoreNeedsObfuscation?: boolean,
) => {
    checkPerformanceReviewTargetUserSerialized(model, dto, scoreNeedsObfuscation);
    checkModelReviewsSerialized(model.reviewedFormReviews, dto.reviewedFormReviews, scoreNeedsObfuscation);
};

export const checkPerformanceReviewCycleSerializedWithUserReviews = (
    model: PerformanceReviewCycle,
    dto: PerformanceReviewCycleWithUserReviewsDTO,
    scoreNeedsObfuscation?: boolean,
) => {
    checkPerformanceReviewCycleSerialized(model, dto);
    checkModelReviewsSerialized(model.reviewerFormReviews, dto.reviewerFormReviews);
    checkModelReviewsSerialized(model.reviewedFormReviews, dto.reviewedFormReviews, scoreNeedsObfuscation);
};

export const checkEditRequestSerialized = (model: PerformanceReviewFormReviewEditRequest, dto: PerformanceReviewFormReviewEditRequestDTO) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.id).toBe(model.id);
    expect(dto.status).toBe(model.status);
    expect(dto.updatedAt).toBe(model.updatedAt);
    expect(dto.requestDescription).toBe(model.requestDescription);
    expect(dto.resolutionDescription).toBe(model.resolutionDescription);
    expect(dto.reviewType).toBe(model.reviewType);
    expect(dto.reviewedId).toBe(model.reviewedId);
    expect(dto.reviewerId).toBe(model.reviewerId);
    expect(dto.reviewFormId).toBe(model.reviewFormId);
    expect(dto.cycleId).toBe(model.cycleId);
    if (!model.reviewed) expect(dto.reviewed).toBeNull();
    else {
        expect(dto.reviewed).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.reviewed, dto.reviewed as PerformanceReviewUserDTO);
    }
    if (!model.reviewer) expect(dto.reviewer).toBeNull();
    else {
        expect(dto.reviewer).not.toBeNull();
        checkPerformanceReviewUserSerialized(model.reviewer, dto.reviewer as PerformanceReviewUserDTO);
    }
};

export const checkTemplateSerialized = (model: PerformanceReviewTemplate, dto: PerformanceReviewTemplateDTO) => {
    expect(dto.createdAt).toBe(model.createdAt);
    expect(dto.formTemplateId).toBe(model.formTemplateId);
    expect(dto.id).toBe(model.id);
    expect(dto.updatedAt).toBe(model.updatedAt);
    expect(dto.questionTemplateId).toBe(model.goalsQuestionTemplateId);
    expect(dto.goalsQuestionTemplateId).toBe(model.goalsQuestionTemplateId);
    expect(dto.competenciesQuestionTemplateId).toBe(model.competenciesQuestionTemplateId);
    expect(dto.questionsCount).toBe(model.questionsCount);
    expect(dto.creatorId).toBe(model.creatorId);
    expect(dto.title).toBe(model.title);
    expect(dto.status).toBe(model.status);
    if (!model.formTemplate) expect(dto.formTemplate).toBeNull();
    else {
        expect(dto.formTemplate).not.toBeNull();
        checkFormTemplateSerialized(model.formTemplate, dto.formTemplate!);
        if (model.formTemplate.questionTemplates) {
            const formTemplateDTO = dto.formTemplate as FormTemplateWithQuestionTemplatesDTO;
            expect(formTemplateDTO.questionTemplates).not.toBeEmpty();
            model.formTemplate.questionTemplates.forEach((questionTemplate, index) => {
                checkQuestionTemplateSerialized(questionTemplate, formTemplateDTO.questionTemplates[index]);
            });
        }
    }
    if (!model.goalsQuestionTemplate) {
        expect(dto.questionTemplate).toBeNull();
        if (model.goalsProgressMode) expect(dto.goalsProgressMode).toBe(PerformanceReviewTemplateGoalsProgressMode.AUTO);
    } else {
        expect(dto.goalsQuestionTemplate).not.toBeNull();
        checkQuestionTemplateSerialized(model.goalsQuestionTemplate, dto.goalsQuestionTemplate!);
        expect(dto.goalsProgressMode).toBe(PerformanceReviewTemplateGoalsProgressMode.MANUAL);
    }
    if (!model.creator) expect(dto.creator).toBeNull();
    else {
        expect(dto.creator).not.toBeNull();
        checkFormTemplateCreatorSerialized(dto.creator!, model.creator);
    }
    if (!model.competenciesQuestionTemplate) {
        expect(dto.competenciesQuestionTemplate).toBeNull();
    } else {
        expect(dto.competenciesQuestionTemplate).not.toBeNull();
        checkQuestionTemplateSerialized(model.competenciesQuestionTemplate, dto.competenciesQuestionTemplate!);
    }
};

const checkAutoReviewForms = (startedCycle: PerformanceReviewCycle, reviewForms: PerformanceReviewFormReview[]) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];

    expect(reviewForms).toHaveLength(evaluatedUserIds.length);
    reviewForms.forEach((reviewForm, index) => {
        expect(reviewForm.cycleId).toBe(startedCycle.id);
        expect(reviewForm.type).toBe(PerformanceReviewEvaluationTypes.AUTO_REVIEW);
        expect(reviewForm.status).toBe(PerformanceReviewFormStatus.PENDING);
        expect(reviewForm.shareStatus).toBe(PerformanceReviewFormShareStatus.SHARE_DISABLED);
        expect(reviewForm.reviewerId).toBe(evaluatedUserIds[index]);
        expect(reviewForm.externalReviewerId).toBeNull();
        expect(reviewForm.reviewedId).toBe(evaluatedUserIds[index]);
        expect(reviewForm.coreFormId).toBeDefined();
        expect(reviewForm.coreFormTag).toBeDefined();
        expect(reviewForm.coreFormAnswerId).toBeNull();
    });
};

const checkSubordinateReviewForms = (
    startedCycle: PerformanceReviewCycle,
    reviewForms: PerformanceReviewFormReview[],
    startParams: InstanceInformationForReviews,
) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];
    const evaluatedUserIdsSet = new Set(evaluatedUserIds);
    const reviewerIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewerId));
    const reviewedIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewedId));

    startParams.instanceBossRelations.forEach((bossRelation) => {
        if (evaluatedUserIdsSet.has(bossRelation.subordinateId)) {
            expect(reviewedIdsSet.has(bossRelation.subordinateId)).toEqual(true);
            expect(reviewerIdsSet.has(bossRelation.bossId)).toEqual(true);
        }
    });
};

const checkBossReviewForms = (
    startedCycle: PerformanceReviewCycle,
    reviewForms: PerformanceReviewFormReview[],
    startParams: InstanceInformationForReviews,
    reviewers: PerformanceReviewReviewer[],
    ignoredReviewerIds: number[],
) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];
    const evaluatedUserIdsSet = new Set(evaluatedUserIds);
    const reviewerIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewerId));
    const reviewedIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewedId));
    startParams.instanceBossRelations.forEach((bossRelation) => {
        if (evaluatedUserIdsSet.has(bossRelation.bossId)) {
            if (startedCycle.reviewerLimitByHiringDate && ignoredReviewerIds.includes(bossRelation.subordinateId)) {
                expect(reviewedIdsSet.has(bossRelation.bossId)).toEqual(true);
                expect(reviewerIdsSet.has(bossRelation.subordinateId)).toEqual(false);
            } else {
                expect(reviewedIdsSet.has(bossRelation.bossId)).toEqual(true);
                expect(reviewerIdsSet.has(bossRelation.subordinateId)).toEqual(true);
            }
        }
    });
};

const checkPeerReviewForms = (
    startedCycle: PerformanceReviewCycle,
    reviewForms: PerformanceReviewFormReview[],
    startParams: InstanceInformationForReviews,
    reviewers: PerformanceReviewReviewer[],
) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];
    const evaluatedUserIdsSet = new Set(evaluatedUserIds);
    const reviewerIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewerId));
    const reviewedIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewedId));

    reviewers
        .filter((reviewer) => reviewer.evaluationType === PerformanceReviewEvaluationTypes.PEER_REVIEW)
        .forEach((reviewer) => {
            if (evaluatedUserIdsSet.has(reviewer.reviewedId)) {
                expect(reviewedIdsSet.has(reviewer.reviewedId)).toBeTrue();
                expect(reviewerIdsSet.has(reviewer.reviewerId!)).toBeTrue();
            }
        });
};

const checkOthersReviewForms = (
    startedCycle: PerformanceReviewCycle,
    reviewForms: PerformanceReviewFormReview[],
    startParams: InstanceInformationForReviews,
    reviewers: PerformanceReviewReviewer[],
) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];
    const evaluatedUserIdsSet = new Set(evaluatedUserIds);
    const reviewerIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewerId));
    const reviewedIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewedId));

    reviewers
        .filter((reviewer) => reviewer.evaluationType === PerformanceReviewEvaluationTypes.OTHERS_REVIEW)
        .forEach((reviewer) => {
            if (evaluatedUserIdsSet.has(reviewer.reviewedId)) {
                expect(reviewedIdsSet.has(reviewer.reviewedId)).toBeTrue();
                expect(reviewerIdsSet.has(reviewer.reviewerId!)).toBeTrue();
            }
        });
};

const checkPeerExternalReviewForms = (
    startedCycle: PerformanceReviewCycle,
    reviewForms: PerformanceReviewFormReview[],
    startParams: InstanceInformationForReviews,
    reviewers: PerformanceReviewReviewer[],
) => {
    const evaluatedUserIds = startedCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? [];
    const evaluatedUserIdsSet = new Set(evaluatedUserIds);
    const reviewerIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.externalReviewerId));
    const reviewedIdsSet = new Set(reviewForms.map((reviewForm) => reviewForm.reviewedId));

    reviewers
        .filter((reviewer) => reviewer.evaluationType === PerformanceReviewEvaluationTypes.PEER_EXTERNAL_REVIEW)
        .forEach((reviewer) => {
            if (evaluatedUserIdsSet.has(reviewer.reviewedId)) {
                expect(reviewedIdsSet.has(reviewer.reviewedId)).toBeTrue();
                expect(reviewerIdsSet.has(reviewer.externalReviewerId!)).toBeTrue();
            }
        });
};

export const checkStartedPerformanceReviewCycle = (
    originalCycle: PerformanceReviewCycle,
    startedCycle: PerformanceReviewCycle,
    startParams: InstanceInformationForReviews,
    reviewers: PerformanceReviewReviewer[],
    ignoredReviewerIds: number[],
) => {
    expect(startedCycle).toBeInstanceOf(PerformanceReviewCycle);
    expect(startedCycle.id).toBe(originalCycle.id);
    expect(startedCycle.createdAt).toBe(originalCycle.createdAt);
    expect(startedCycle.name).toBe(originalCycle.name);
    expect(startedCycle.instanceId).toBe(originalCycle.instanceId);
    expect(startedCycle.status).toBe(PerformanceReviewCycleStatus.IN_PROGRESS);
    expect(startedCycle.reviewedUsersMode).toBe(PerformanceReviewCycleReviewedUsersMode.TARGETED_USERS);
    expect(startedCycle.startDate).toBeInstanceOf(Date);
    expect(startedCycle.endDate).toBeNull();

    const targetUserIds =
        originalCycle.reviewedUsersMode === PerformanceReviewCycleReviewedUsersMode.TARGETED_USERS
            ? originalCycle.targetUsers?.map((targetUser) => targetUser.userId) ?? []
            : startParams.instanceUserIds;
    expect(startedCycle.targetUsers).not.toBeNull();
    expect(startedCycle.targetUsers).toHaveLength(targetUserIds.length);
    (startedCycle.targetUsers as PerformanceReviewTargetUser[]).forEach((targetUser, index) => {
        expect(targetUser).toBeInstanceOf(PerformanceReviewTargetUser);
        expect(targetUser.userId).toBe(targetUserIds[index]);
    });

    const originalFormTemplates = originalCycle.formTemplates ?? [];
    expect(startedCycle.formTemplates).not.toBeNull();
    expect(startedCycle.formTemplates).toHaveLength(originalFormTemplates.length);
    (startedCycle.formTemplates as PerformanceReviewFormTemplate[]).forEach((formTemplate, index) => {
        expect(formTemplate).toBeInstanceOf(PerformanceReviewFormTemplate);
        expect(formTemplate.templateId).toBe(originalFormTemplates[index].templateId);
        expect(formTemplate.coreFormId).toBeDefined();
        expect(formTemplate.coreFormTag).toBeDefined();
    });

    expect(startedCycle.reviewForms).not.toBeNull();
    const formsPerType = (startedCycle.reviewForms as PerformanceReviewFormReview[]).reduce(
        (records: Record<PerformanceReviewEvaluationTypesValues, PerformanceReviewFormReview[]>, reviewForm) => {
            if (!records[reviewForm.type]) records[reviewForm.type] = [];
            records[reviewForm.type].push(reviewForm);
            return records;
        },
        {} as Record<PerformanceReviewEvaluationTypesValues, PerformanceReviewFormReview[]>,
    );

    const checkersPerEvaluationType = {
        [PerformanceReviewEvaluationTypes.AUTO_REVIEW]: checkAutoReviewForms,
        [PerformanceReviewEvaluationTypes.SUBORDINATE_REVIEW]: checkSubordinateReviewForms,
        [PerformanceReviewEvaluationTypes.BOSS_REVIEW]: checkBossReviewForms,
        [PerformanceReviewEvaluationTypes.PEER_REVIEW]: checkPeerReviewForms,
        [PerformanceReviewEvaluationTypes.PEER_EXTERNAL_REVIEW]: checkPeerExternalReviewForms,
        [PerformanceReviewEvaluationTypes.OTHERS_REVIEW]: checkOthersReviewForms,
    };
    originalFormTemplates.forEach((formTemplate) => {
        const typeReviews = formsPerType[formTemplate.evaluationType] ?? [];
        checkersPerEvaluationType[formTemplate.evaluationType](startedCycle, typeReviews, startParams, reviewers, ignoredReviewerIds);
    });
};

export const checkCycleFormReviewsHaveUsers = (formReviews: PerformanceReviewFormReview[]) => {
    formReviews.forEach((reviewForm) => {
        expect(reviewForm.reviewed).not.toBeNull();
        expect(reviewForm.reviewed).toBeInstanceOf(PerformanceReviewUser);
        expect(reviewForm.reviewed?.userId).toBe(reviewForm.reviewedId);

        expect(reviewForm.reviewer).not.toBeNull();
        expect(reviewForm.reviewer).toBeInstanceOf(PerformanceReviewUser);
        expect(reviewForm.reviewer?.userId).toBe(reviewForm.reviewerId);
    });
};

export const generatePerformanceReviewReviewer = (values: {
    id?: number;
    cycleId?: number;
    reviewedId?: number;
    reviewerId?: number;
    externalReviewerId?: number;
    externalReviewerCount?: number;
    reviewedCount?: number;
    reviewerCount?: number;
    evaluationType?: PerformanceReviewPeerEvaluationTypesValues;
}) => {
    return new PerformanceReviewReviewer({
        createdAt: faker.date.recent(),
        cycleId: values.cycleId ?? fakePositiveInteger(),
        externalReviewerCount: values.externalReviewerCount ?? fakePositiveInteger(),
        externalReviewerId: values.externalReviewerId ?? fakePositiveInteger(),
        id: values.id ?? fakePositiveInteger(),
        reviewedCount: values.reviewedCount ?? fakePositiveInteger(),
        reviewedId: values.reviewedId ?? fakePositiveInteger(),
        reviewerCount: values.reviewerCount ?? fakePositiveInteger(),
        reviewerId: values.reviewerId ?? fakePositiveInteger(),
        updatedAt: faker.date.recent(),
        evaluationType: values.evaluationType ?? faker.helpers.arrayElement(Object.values(PerformanceReviewPeerEvaluationTypes)),
    });
};

export const generateExternalPerformanceReviewReviewer = () => {
    return new PerformanceReviewReviewer({
        createdAt: faker.date.recent(),
        cycleId: fakePositiveInteger(),
        externalReviewerCount: fakePositiveInteger(),
        externalReviewerId: fakePositiveInteger(),
        id: fakePositiveInteger(),
        reviewedCount: fakePositiveInteger(),
        reviewedId: fakePositiveInteger(),
        reviewerId: null,
        updatedAt: faker.date.recent(),
        evaluationType: PerformanceReviewEvaluationTypes.PEER_EXTERNAL_REVIEW,
    });
};

export const generateInternalPerformanceReviewReviewer = () => {
    return new PerformanceReviewReviewer({
        createdAt: faker.date.recent(),
        cycleId: fakePositiveInteger(),
        externalReviewerId: null,
        id: fakePositiveInteger(),
        reviewedCount: fakePositiveInteger(),
        reviewedId: fakePositiveInteger(),
        reviewerCount: fakePositiveInteger(),
        reviewerId: fakePositiveInteger(),
        updatedAt: faker.date.recent(),
        evaluationType: faker.helpers.arrayElement(Object.values([PerformanceReviewEvaluationTypes.PEER_REVIEW, PerformanceReviewEvaluationTypes.OTHERS_REVIEW])),
    });
};

export const generateRandomPerformanceReviewReviewerForCreation = () => {
    return new PerformanceReviewReviewerForCreation({
        cycleId: fakePositiveInteger(),
        externalReviewerId: fakePositiveInteger(),
        reviewedId: fakePositiveInteger(),
        reviewerId: fakePositiveInteger(),
        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewPeerEvaluationTypes)),
    });
};

export const generatePerformanceReviewReviewerFromCreation = (reviewer: PerformanceReviewReviewerForCreation) => {
    return new PerformanceReviewReviewer({
        createdAt: faker.date.recent(),
        cycleId: reviewer.cycleId,
        externalReviewerId: reviewer.externalReviewerId,
        id: fakePositiveInteger(),
        reviewedId: reviewer.reviewedId,
        reviewerId: reviewer.reviewerId,
        updatedAt: faker.date.recent(),
        evaluationType: reviewer.evaluationType,
    });
};

export const generatePerformanceReviewReviewerDAO = (model: PerformanceReviewReviewer) => {
    const dao = new PerformanceReviewReviewerDAO();
    dao.id = model.id;
    dao.createdAt = model.createdAt;
    dao.updatedAt = model.updatedAt;
    dao.cycleId = model.cycleId;
    dao.externalReviewerId = model.externalReviewerId;
    dao.externalReviewerCount = model.externalReviewerCount ?? undefined;
    dao.reviewerCount = model.reviewerCount ?? undefined;
    dao.reviewedCount = model.reviewedCount ?? undefined;
    dao.reviewedId = model.reviewedId;
    dao.reviewerId = model.reviewerId;
    return dao;
};

export const checkPerformanceReviewReviewedSerialized = (
    model: {
        reviewed: PerformanceReviewReviewer | null;
        user: PerformanceReviewUser;
        bosses: User[];
    },
    dto: PerformanceReviewReviewedDTO,
) => {
    const internalCount = model.reviewed?.reviewerCount ?? 0;
    const externalCount = model.reviewed?.externalReviewerCount ?? 0;
    expect(dto.firstName).toBe(model.user.firstName);
    expect(dto.lastName).toBe(model.user.lastName);
    expect(dto.userId).toBe(model.user.userId);
    expect(dto.responsibleFirstName).toBe(model.bosses.length > 0 ? model.bosses[0].firstName : null);
    expect(dto.responsibleLastName).toBe(model.bosses.length > 0 ? model.bosses[0].lastName : null);
    expect(dto.reviewerCount).toBe(internalCount + externalCount);
    expect(dto.profilePicture).toBe(model.user.profilePicture);
    expect(dto.userStatus).toBe(model.user.userStatus);
};

export const checkPerformanceReviewReviewerSerialized = (
    dto: PerformanceReviewReviewerDTO,
    externalReviewersMap: Map<number, PerformanceReviewExternalReviewer>,
    internalReviewersMap: Map<number, PerformanceReviewUser>,
    reviewer: PerformanceReviewReviewer,
) => {
    const reviewerMapped = reviewer.isExternal()
        ? externalReviewersMap.get(reviewer.externalReviewerId!)!
        : internalReviewersMap.get(reviewer.reviewerId!)!;
    const isExternal = reviewerMapped instanceof PerformanceReviewExternalReviewer;
    expect(dto.firstName).toBe(reviewerMapped.firstName);
    expect(dto.lastName).toBe(reviewerMapped.lastName);
    expect(dto.isExternal).toBe(reviewer.isExternal());
    expect(dto.userId).toBe(isExternal ? reviewerMapped.id : reviewerMapped.userId);
    expect(dto.profilePicture).toBe(isExternal ? null : reviewerMapped.profilePicture);
    expect(dto.reviewedCount).toBe(reviewer.reviewedCount);
};

export const checkPerformanceReviewReviewerSelectionStatsSerialized = (
    model: PerformanceReviewSelectionStats,
    dto: PerformanceReviewSelectionStatsDTO,
) => {
    expect(dto.totalTargetUsers).toBe(model.totalTargetUsers);
    expect(dto.targetUsersWithReviewedAssigned).toBe(model.totalTargetUsersWithReviewerAssigned);
};

export const generateDefaultScoreDetails = (): ScoreDetailsVC => {
    return generateScoreDetailsWithValues({});
};

export const generateScoreDetailsWithValues = ( values: {
    scaleDetails?: ScaleDetailsVC;
    withHiddenScoreForReviewed?: boolean;
    qualitativeResultRanges?: QualitativeResultRangeVC[];
    scoreDisplayType?: ScoreDisplayTypeValues;
}
): ScoreDetailsVC => {
    const scaleDetails = { end: PerformanceReviewFormTemplate.getDefaultScoreConfig().scaleEnd, start: PerformanceReviewFormTemplate.getDefaultScoreConfig().scaleStart };
    return {
        scaleDetails: values.scaleDetails ?? scaleDetails,
        withHiddenScoreForReviewed: values.withHiddenScoreForReviewed ?? false,
        qualitativeResultRanges: values.qualitativeResultRanges ?? null,
        scoreDisplayType: values.scoreDisplayType ?? ScoreDisplayType.NUMBER_ONLY,
    };
};

export const generateRandomScoreDetails = (): ScoreDetailsVC => {
    const start = fakePositiveInteger({ min: 0, max: 1 });
    const end = fakePositiveInteger({ min: 5, max: 100 });
    return generateScoreDetailsWithValues({
        scaleDetails: { end, start },
        withHiddenScoreForReviewed: faker.datatype.boolean(),
        qualitativeResultRanges: generateRandomQualitativeRanges(start, end),
        scoreDisplayType: faker.helpers.arrayElement(Object.values(ScoreDisplayType)),
    });
};

export const generateRandomQualitativeRanges = (
    rangeStart: number,
    rangeEnd: number
  ): QualitativeResultRangeVC[] => {
    const numberOfRanges = fakePositiveInteger({ min: 2, max: 5 });
  
    const cutPoints: number[] = [];
  
    // generar cortes internos enteros y únicos
    while (cutPoints.length < numberOfRanges - 1) {
      const cut = fakePositiveInteger({ min: rangeStart + 1, max: rangeEnd - 1 });
      if (!cutPoints.includes(cut)) {
        cutPoints.push(cut);
      }
    }
  
    cutPoints.sort((a, b) => a - b);
  
    const boundaries = [rangeStart, ...cutPoints, rangeEnd];
  
    const ranges: QualitativeResultRangeVC[] = boundaries
      .slice(0, -1)
      .map((start, i) => ({
        start,
        end: boundaries[i + 1],
        tag: faker.word.noun(),
      }));
  
    return ranges;
  };


export const generateRandomPerformanceReviewDirectionWeight = (directions: {evaluationType: PerformanceReviewEvaluationTypesValues, weightDetails: WeightDetails}[]): {
    list: { evaluationType: PerformanceReviewEvaluationTypesValues; weight: number; weightDetails: WeightDetails; }[];
    map: Map<PerformanceReviewEvaluationTypesValues, number>;
  } => {
    const n = directions.length;
    if (n <= 0 || n > 100) {
      throw new Error("The number of types must be between 1 and 100.");
    }
  
    const cuts = new Set<number>();
  
    // Generate exactly n - 1 unique cut points between 1 and 99
    while (cuts.size < n - 1) {
      cuts.add(Math.floor(Math.random() * 99) + 1);
    }
  
    // Add boundaries and sort
    const points = [...cuts, 0, 100].sort((a, b) => a - b);
  
  // Calculate weights and map to evaluation types
  const list = directions.map((direction, i) => ({
    evaluationType: direction.evaluationType,
    weight: points[i + 1] - points[i],
    weightDetails: direction.weightDetails,
  }));

  const map = new Map(
    list.map(item => [item.evaluationType, item.weight])
  );

  return { list, map };
};

export const formTemplateToFormTemplateParams = (formTemplate: FormTemplate): UpsertFormTemplateInterface => {
    return {
        ...formTemplate,
        description: formTemplate.description ?? undefined,

        questionTemplates: formTemplate.questionTemplates
            ? formTemplate.questionTemplates.map((q) => ({
                  ...q,
                  description: q.description ?? undefined,
                  topics: q.topics?.map((topic) => topic.id) ?? undefined,
              }))
            : null,
    };
};
