import { faker } from '@faker-js/faker/locale/en_US';
import { PerformanceReviewEvaluationTypes } from '../../../../src/api/modules/performanceReviews/business/types/performanceReviewEvaluationTypes';
import { PerformanceReviewScoreConfigVC } from '../../../../src/api/modules/performanceReviews/presentation/validationClasses/performanceReviewScoreConfigVC';
import { fakePositiveInteger } from '../../../common/common';
import { getValidationClass } from '../../../common/serializationClasses/serializationClassesHelper';
import { generateRandomScoreDetails } from '../common';

describe('PerformanceReviewScoreConfigVC', () => {
    it(`should pass validation if the user sends no score details`, () => {
        const { errors, output } = getValidationClass(
            {
                directions: [],
                scoreDetails: null,
            },
            PerformanceReviewScoreConfigVC,
        );

        expect(errors).toHaveLength(0);
        expect(output.scoreDetails).toBeNull();
    });

    it(`should pass validation if the user sends null score details`, () => {
        const { errors } = getValidationClass(
            {
                directions: [],
                scoreDetails: null,
            },
            PerformanceReviewScoreConfigVC,
        );

        expect(errors).toHaveLength(0);
    });

    it(`should validate correctly if the user sends no score details with scores`, () => {
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails: {
                            formQuestionWeights: [],
                            formWeight: fakePositiveInteger(),
                            goalsWeight: fakePositiveInteger(),
                        },
                    },
                ],
                scoreDetails: null,
            },
            PerformanceReviewScoreConfigVC,
        );

        expect(errors).toPartiallyContain({ property: 'scoreDetails' });
    });

    it(`should validate correctly if the user sends null weight details with weighted score`, () => {
        const scoreDetails = generateRandomScoreDetails();

        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails: null,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsWeightDetailsError = JSON.stringify(errors).includes('weightDetails');

        expect(containsWeightDetailsError).toBe(true);
    });

    it(`should validate correctly if the user sends null form question weights`, () => {
        const weightDetails = {
            formQuestionWeights: null,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scoreDetails = generateRandomScoreDetails();
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsFormQuestionWeightsError = JSON.stringify(errors).includes('formQuestionWeights');
        expect(containsFormQuestionWeightsError).toBe(true);
    });

    it(`should validate correctly if the user sends null formWeight`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: null,
            goalsWeight: fakePositiveInteger(),
        };
        const scoreDetails = generateRandomScoreDetails();
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsFormWeightError = JSON.stringify(errors).includes('formWeight');

        expect(containsFormWeightError).toBe(true);
    });

    it(`should validate correctly if the user sends null goalsWeight`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: null,
        };
        const scoreDetails = generateRandomScoreDetails();
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsGoalsWeightError = JSON.stringify(errors).includes('goalsWeight');

        expect(containsGoalsWeightError).toBe(true);
    });

    it('should validate correctly if the user sends incorrect types', () => {
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.word.words(),
                        weight: faker.word.words(),
                    },
                ],
                scoreDetails: faker.word.words(),
            },
            PerformanceReviewScoreConfigVC,
        );

        ['evaluationType', 'weight', 'scoreDetails'].forEach((property) => {
            const containsPropertyError = JSON.stringify(errors).includes(property);

            expect(containsPropertyError).toBe(true);
        });
    });

    it(`should validate correctly if the user sends incorrect question weights without scores`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: null,
        };
        const scoreDetails = generateRandomScoreDetails();
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsFormQuestionWeightsError = JSON.stringify(errors).includes('formQuestionWeights');

        expect(containsFormQuestionWeightsError).toBe(true);
    });

    it(`should validate correctly if the user not sends scale details`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scoreDetails = {
            withHiddenScoreForReviewed: false,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsScaleDetailsError = JSON.stringify(errors).includes('scaleDetails');

        expect(containsScaleDetailsError).toBe(true);
    });

    it(`should validate correctly if user sends scale details as null`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scoreDetails = {
            scaleDetails: null,
            withHiddenScoreForReviewed: false,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsScaleDetailsError = JSON.stringify(errors).includes('scaleDetails');

        expect(containsScaleDetailsError).toBe(true);
    });

    it(`should validate correctly if user sends scale details with no start or end`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scaleDetails = {
            end: null,
            start: null,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: false,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        ['start', 'end'].forEach((property) => {
            const containsPropertyError = JSON.stringify(errors).includes(property);

            expect(containsPropertyError).toBe(true);
        });
    });

    it(`should validate correctly if user sends scale details with negative start`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scaleDetails = {
            end: 10,
            start: -10,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: false,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsStartError = JSON.stringify(errors).includes('start');

        expect(containsStartError).toBe(true);
    });

    it(`should validate correctly if user sends scale details with end lower than start`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scaleDetails = {
            end: 5,
            start: 10,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: false,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsEndError = JSON.stringify(errors).includes('end');

        expect(containsEndError).toBe(true);
    });

    it(`should validate correctly if user sends scale details with end equal to start`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scaleDetails = {
            end: 10,
            start: 10,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: true,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        const containsEndError = JSON.stringify(errors).includes('end');

        expect(containsEndError).toBe(true);
    });

    it(`should validate correctly if number fields are no integers`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: 40.5,
            goalsWeight: 59.5,
        };
        const scaleDetails = {
            end: 10.1,
            start: 1.1,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: true,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        ['start', 'end', 'formWeight', 'goalsWeight'].forEach((property) => {
            const containsPropertyError = JSON.stringify(errors).includes(property);

            expect(containsPropertyError).toBe(true);
        });
    });

    it(`full payload should pass validation`, () => {
        const referenceIds = faker.helpers.multiple(fakePositiveInteger);
        const formQuestionWeights = referenceIds.map((referenceId) => ({
            referenceId,
            weight: fakePositiveInteger(),
        }));
        const weightDetails = {
            formQuestionWeights,
            formWeight: fakePositiveInteger(),
            goalsWeight: fakePositiveInteger(),
        };
        const scaleDetails = {
            end: 10,
            start: 0,
        };
        const scoreDetails = {
            scaleDetails,
            withHiddenScoreForReviewed: true,
        };
        const { errors } = getValidationClass(
            {
                directions: [
                    {
                        evaluationType: faker.helpers.arrayElement(Object.values(PerformanceReviewEvaluationTypes)),
                        weight: 100,
                        weightDetails,
                    },
                ],
                scoreDetails,
            },
            PerformanceReviewScoreConfigVC,
        );

        expect(errors).toHaveLength(0);
    });
});
