package io.katharsis.resource;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.katharsis.resource.annotations.JsonApiId;
import io.katharsis.resource.annotations.JsonApiResource;
import io.katharsis.resource.exception.init.ResourceDuplicateIdException;
import io.katharsis.resource.exception.init.ResourceIdNotFoundException;
import io.katharsis.resource.mock.models.Task;
import io.katharsis.resource.mock.models.UnAnnotatedTask;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.assertj.core.api.Assertions.assertThat;

public class ResourceInformationBuilderTest {

    private static final String NAME_PROPERTY = "name";

    @Rule
    public ExpectedException expectedException = ExpectedException.none();

    private ResourceInformationBuilder resourceInformationBuilder = new ResourceInformationBuilder(
        new ResourceFieldNameTransformer());

    @Test
    public void shouldHaveResourceClassInfoForValidResource() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(Task.class);

        assertThat(resourceInformation.getResourceClass())
            .isNotNull()
            .isEqualTo(Task.class);
    }

    @Test
    public void shouldHaveIdFieldInfoForValidResource() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(Task.class);

        assertThat(resourceInformation.getIdField().getName())
            .isNotNull()
            .isEqualTo("id");
    }

    @Test
    public void shouldThrowExceptionWhenResourceWithNoIdAnnotation() {
        expectedException.expect(ResourceIdNotFoundException.class);

        resourceInformationBuilder.build(UnAnnotatedTask.class);
    }

    @Test
    public void shouldThrowExceptionWhenMoreThan1IdAnnotationFound() throws Exception {
        expectedException.expect(ResourceDuplicateIdException.class);
        expectedException.expectMessage("Duplicated Id field found in class");

        resourceInformationBuilder.build(DuplicatedIdResource.class);
    }

    @Test
    public void shouldHaveProperBasicFieldInfoForValidResource() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(Task.class);

        assertThat(resourceInformation.getAttributeFields())
            .isNotNull()
            .hasSize(1)
            .extracting(NAME_PROPERTY)
            .containsOnly("name");
    }

    @Test
    public void shouldHaveProperRelationshipFieldInfoForValidResource() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(Task.class);

        assertThat(resourceInformation.getRelationshipFields())
            .isNotNull()
            .hasSize(2)
            .extracting(NAME_PROPERTY)
            .contains("project", "projects");
    }

    @Test
    public void shouldThrowExceptionWhenResourceWithIgnoredIdAnnotation() {
        expectedException.expect(ResourceIdNotFoundException.class);

        resourceInformationBuilder.build(IgnoredIdResource.class);
    }

    @Test
    public void shouldThrowExceptionWhenResourceWithTransientIdAnnotation() {
        expectedException.expect(ResourceIdNotFoundException.class);

        resourceInformationBuilder.build(TransientIdResource.class);
    }

    @Test
    public void shouldHaveNoAttributesInfoForIgnoredField() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(AccessorGetterResource.class);

        assertThat(resourceInformation.getAttributeFields())
            .isNotNull()
            .hasSize(1)
            .extracting(NAME_PROPERTY)
            .containsOnly("accessorField");
    }

    @Test
    public void shouldNotReturnFieldBasedOnAccessorGetterWhenGetterIsIgnored() throws Exception {
        ResourceInformation resourceInformation = resourceInformationBuilder.build(IgnoredAccessorGetterResource.class);

        assertThat(resourceInformation.getAttributeFields())
            .isNotNull()
            .isEmpty();
    }

    @JsonApiResource(type = "duplicatedIdAnnotationResources")
    private static class DuplicatedIdResource {
        @JsonApiId
        private Long id;

        @JsonApiId
        private Long id2;
    }

    @JsonApiResource(type = "ignoredId")
    private static class IgnoredIdResource {
        @JsonApiId
        @JsonIgnore
        private Long id;
    }

    @JsonApiResource(type = "transientId")
    private static class TransientIdResource {
        @JsonApiId
        private transient Long id;
    }

    @JsonApiResource(type = "ignoredAttribute")
    private static class IgnoredAttributeResource {
        @JsonApiId
        private Long id;

        @JsonIgnore
        private String attribute;
    }

    @JsonApiResource(type = "accessorGetter")
    private static class AccessorGetterResource {
        @JsonApiId
        private Long id;

        private String getAccessorField() {
            return null;
        }
    }

    @JsonApiResource(type = "ignoredAccessorGetter")
    private static class IgnoredAccessorGetterResource {
        @JsonApiId
        private Long id;

        @JsonIgnore
        private String getAccessorField() {
            return null;
        }
    }
}

@@ -4085,32 +4085,452 @@
 Empty();%0A    %7D%0A%0A
+    @Test%0A    public void shouldReturnFieldBasedOnFieldOnlyAndIgnoreGetter() throws Exception %7B%0A        ResourceInformation resourceInformation = resourceInformationBuilder.build(FieldWithAccessorGetterResource.class);%0A%0A        assertThat(resourceInformation.getAttributeFields())%0A            .isNotNull()%0A            .hasSize(1)%0A            .extracting(NAME_PROPERTY)%0A            .containsOnly(%22accessorField%22);%0A    %7D%0A%0A
     @JsonApiReso
@@ -5721,11 +5721,311 @@
  %7D%0A    %7D
+%0A%0A    @JsonApiResource(type = %22fieldWithAccessorGetterResource%22)%0A    private static class FieldWithAccessorGetterResource %7B%0A        @JsonApiId%0A        private Long id;%0A%0A        public String getAccessorField() %7B%0A            return accessorField;%0A        %7D%0A%0A        private String accessorField;%0A    %7D
 %0A%7D%0A

Support annotations search #70