package io.delphiplatform.api.v3.validation;

import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorContextImpl;
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.metadata.location.ConstraintLocation.ConstraintLocationKind;
import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Set;

import javax.validation.Payload;

import io.delphiplatform.api.v3.constant.ApplicationConstants;

class ExactMatchesValidatorForSingleValueTest {

    @Test
    void isValidSimple() {
        ExactMatchesValidatorForSingleValue validator = new ExactMatchesValidatorForSingleValue();
        ExactMatchesForSingleValue exactMatchesForSingleValue = getConstraintAnnotation("artist_id", "artist_name");
        validator.initialize(exactMatchesForSingleValue);
        ConstraintAnnotationDescriptor<ExactMatchesForSingleValue> constraintAnnotationDescriptor = new ConstraintAnnotationDescriptor<>(
            exactMatchesForSingleValue);
        ConstraintHelper constraintHelper = ConstraintHelper
            .forBuiltinConstraints(Set.of("javax.validation.constraints.AssertFalse"));

        ConstraintDescriptorImpl<ExactMatchesForSingleValue> constraintDescriptor = new ConstraintDescriptorImpl<>(
            constraintHelper, null,
            constraintAnnotationDescriptor, ConstraintLocationKind.PARAMETER);

        ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl(null, null, constraintDescriptor,
            null, null, null);

        Assertions.assertTrue(validator.isValid(null, context));
        Assertions.assertTrue(validator.isValid("artist_name", context));
        Assertions.assertTrue(validator.isValid("artist_id", context));
        //all previous calls do not lead to the parameter settings
        Assertions.assertTrue(context.getConstraintViolationCreationContexts().get(0).getMessageParameters().isEmpty());
        Assertions.assertFalse(validator.isValid("artist_full_name", context));
        //parameters were set after failed validation
        Map<String, Object> params = context.getConstraintViolationCreationContexts().get(0).getMessageParameters();
        Assertions.assertEquals(1, params.size());
        Assertions.assertTrue(params.containsKey(ApplicationConstants.ALLOWED_VALUES_PLACEHOLDER));
        Set<String> set = (Set<String>) params.get(ApplicationConstants.ALLOWED_VALUES_PLACEHOLDER);
        Assertions.assertTrue(set.contains("artist_id"));
        Assertions.assertTrue(set.contains("artist_name"));
    }

    private ExactMatchesForSingleValue getConstraintAnnotation(final String... strings) {
        return new ExactMatchesForSingleValue() {

            @Override
            public Class<? extends Annotation> annotationType() {
                return ExactMatchesForSingleValue.class;
            }

            @Override
            public String message() {
                return ApplicationConstants.ERROR_MESSAGE_SORT_BY_PARAMETER;
            }

            @Override
            public Class<?>[] groups() {
                return new Class[0];
            }

            @Override
            public Class<? extends Payload>[] payload() {
                return new Class[]{this.getClass()};
            }

            @Override
            public String[] anyOf() {
                return strings;
            }
        };
    }
}
