package architecturetests;

import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.EvaluationResult;
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction;
import de.accso.flexinale.shared_kernel.*;
import org.junit.jupiter.api.Test;

import java.util.Set;

import static architecturetests.ArchUnitHelper.*;
import static org.assertj.core.api.Assertions.assertThat;

public class InternalStructureOfDomainClassesUsingCorrectInterfacesAndAttributeTypesTest {

    static final String PACKAGE_DOMAIN_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".domain.model..";

    static final GivenClassesConjunction domainClasses = ArchRuleDefinition.classes()
            .that()
            .resideInAPackage(PACKAGE_DOMAIN_MODEL)
            .and()
            .areNotEnums()
            .and()
            .areNotInnerClasses()
            .and()
            .areNotNestedClasses()
            .and()
            .areNotAnnotatedWith(DoNotCheckInArchitectureTests.class);

    @Test
    void test_DomainClassesAreImplementingIdentifiableAndVersionableAndEqualsByContent() {
        // arrange
        ArchRule archRule = domainClasses
                .should()
                .implement(Identifiable.class)
                .andShould()
                .implement(Versionable.class)
                .andShould()
                .implement(EqualsByContent.class);

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }

    @Test
    void test_DomainClassesHaveIdFieldUsingStrongType() {
        // arrange
        String idField = "id";

        ArchRule archRule = domainClasses
                .should(hasFieldOfType(idField, Identifiable.Id.class));

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }

    @Test
    void test_DomainClassesHaveVersionFieldUsingStrongType() {
        // arrange
        String versionField = "version";

        ArchRule archRule = domainClasses
                .should(hasFieldOfType(versionField, Versionable.Version.class));

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }

    @Test
    void test_DomainClassesHaveIdFieldWhichIsPublicFinal() {
        // arrange
        String idField = "id";

        ArchRule archRule = domainClasses
                .should(hasFieldPublicFinal(idField));

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }

    @Test
    void test_DomainClassesHaveVersionFieldWhichIsPublicFinal() {
        // arrange
        String versionField = "version";

        ArchRule archRule = domainClasses
                .should(hasFieldPublicFinal(versionField));

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }

    @Test
    void test_DomainClassesHaveOnlyFieldsImplementingRawWrapper() {
        // arrange
        Set<Class<?>>        interfaceTypes = Set.of(RawWrapper.class);
        Set<String>   fieldsToBeFilteredOut = Set.of();
        String fieldSuffixToBeFilteredOut   = "Id";

        ArchRule archRule = domainClasses
                .should(hasOnlyFieldsImplementing(interfaceTypes, fieldsToBeFilteredOut, fieldSuffixToBeFilteredOut));

        // act
        EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);

        // assert
        assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
    }
}
