package uk.ac.ebi.quickgo.annotation.model;

import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.Before;
import org.junit.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;

/**
 * Tests that the validation added to the {@link AnnotationRequest} class is correct.
 */
public class AnnotationRequestValidationIT {
    private static final String[] VALID_ASSIGNED_BY_PARMS = {"ASPGD", "ASPGD,Agbase", "ASPGD_,Agbase",
            "ASPGD,Agbase_", "ASPGD,Agbase", "BHF-UCL,Agbase", "Roslin_Institute,BHF-UCL,Agbase"};

    private static final String[] INVALID_ASSIGNED_BY_PARMS = {"_ASPGD", "ASPGD,_Agbase",
            "5555,Agbase", "ASPGD,5555,", "4444,5555,"};

    private static final String[] VALID_GO_EVIDENCE = {"IEA,IBD,IC"};
    private static final String[] INVALID_GO_EVIDENCE = {"9EA,IBDD,I"};


    private Validator validator;
    private AnnotationRequest annotationRequest;

    @Before
    public void setUp() throws Exception {
        ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
        validator = vf.getValidator();

        annotationRequest = new AnnotationRequest();
    }

    //ASSIGNED BY PARAMETER
    @Test
    public void nullAssignedByIsValid() {
        annotationRequest.setAssignedBy(null);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void allAssignedByValuesAreValid() {
        String assignedByValues = Arrays.stream(VALID_ASSIGNED_BY_PARMS)
                .collect(Collectors.joining(","));

        annotationRequest.setAssignedBy(assignedByValues);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void allAssignedByValuesAreInvalid() {
        Arrays.stream(INVALID_ASSIGNED_BY_PARMS).forEach(
                invalidValue -> {
                    AnnotationRequest annotationRequest = new AnnotationRequest();
                    annotationRequest.setAssignedBy(invalidValue);

                    assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
                }
        );
    }

    //GO EVIDENCE
    @Test
    public void nullGoEvidenceIsValid() {
        annotationRequest.setGoEvidence(null);
        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void allGoEvidenceValuesAreValid() {
        for (String valid: VALID_ASSIGNED_BY_PARMS) {
            annotationRequest.setGoEvidence(valid);
            assertThat(valid + " expected to be a valid value, but has failed validation",validator.validate
                    (annotationRequest), hasSize(0));
        }

    }

    @Test
    public void allGoEvidenceValuesAreInvalid() {
        Arrays.stream(INVALID_GO_EVIDENCE).forEach(
                invalidValue -> {
                    AnnotationRequest annotationRequest = new AnnotationRequest();
                    annotationRequest.setGoEvidence(invalidValue);
                    assertThat(invalidValue + " expected to be an invalid value, but it has passed " +
                            "validation", validator.validate(annotationRequest), hasSize(greaterThan(0)));
                }
        );
    }

    //ASPECT PARAMETER
    @Test
    public void nullAspectIsValid() {
        String aspect = null;

        annotationRequest.setAspect(aspect);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void processAspectIsValid() {
        String aspect = "biological_process";

        annotationRequest.setAspect(aspect);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void functionAspectIsValid() {
        String aspect = "molecular_function";

        annotationRequest.setAspect(aspect);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void componentAspectIsValid() {
        String aspect = "cellular_component";

        annotationRequest.setAspect(aspect);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void unknownAspectIsInvalid() {
        String aspect = "unknown";

        annotationRequest.setAspect(aspect);

        assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
    }

    @Test
    public void mixedCaseAspectIsValid() {
        String[] aspects = {"MoLeCuLaR_FuNcTiOn", "BiOlOgIcAl_pRoCeSs", "CelLUlar_CoMpOnEnT"};

        Arrays.stream(aspects).forEach(
                mixedCaseAspect -> {
                    annotationRequest.setAspect(mixedCaseAspect);

                    Set<ConstraintViolation<AnnotationRequest>> violations = validator.validate(annotationRequest);
                    printConstraintViolations(violations);

                    assertThat(violations, hasSize(0));
                }
        );
    }

    //PAGE PARAMETER
    @Test
    public void negativePageValueIsInvalid() {
        annotationRequest.setPage(-1);

        assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
    }

    @Test
    public void zeroPageValueIsInvalid() {
        annotationRequest.setPage(0);

        assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
    }

    @Test
    public void positivePageValueIsValid() {
        annotationRequest.setPage(1);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    //LIMIT PARAMETER
    @Test
    public void negativeLimitValueIsInvalid() {
        annotationRequest.setLimit(-1);

        assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
    }

    @Test
    public void zeroLimitValueIsValid() {
        annotationRequest.setLimit(0);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void positiveLimitValueIsValid() {
        annotationRequest.setLimit(1);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void limitValueEqualToMaxEntriesPerPageIsInvalid() {
        annotationRequest.setLimit(AnnotationRequest.MAX_ENTRIES_PER_PAGE);

        assertThat(validator.validate(annotationRequest), hasSize(0));
    }

    @Test
    public void limitValueAboveMaxEntriesPerPageIsInvalid() {
        annotationRequest.setLimit(AnnotationRequest.MAX_ENTRIES_PER_PAGE + 1);

        assertThat(validator.validate(annotationRequest), hasSize(greaterThan(0)));
    }

    private void printConstraintViolations(Set<ConstraintViolation<AnnotationRequest>> violations) {
        violations.stream().forEach(System.out::println);
    }
}

@@ -1109,17 +1109,16 @@
 D,I%22%7D;%0A%0A
-%0A
     priv
@@ -2664,16 +2664,17 @@
 alid
+ 
 : VALID_
 ASSI
@@ -2669,33 +2669,27 @@
 : VALID_
-ASSIGNED_BY_PARMS
+GO_EVIDENCE
 ) %7B%0A    
@@ -2826,34 +2826,16 @@
 dation%22,
-validator.validate
 %0A       
@@ -2847,16 +2847,34 @@
         
+validator.validate
 (annotat
@@ -2905,25 +2905,24 @@
 ;%0A        %7D%0A
-%0A
     %7D%0A%0A    @

Fix broken test cases.