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.Identifiable;
import org.junit.jupiter.api.Test;

import java.io.Serializable;

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

public class InternalStructureOfEntityClassesUsingCorrectInterfacesAndAttributeTypesTest {

    static final GivenClassesConjunction entities = ArchRuleDefinition.classes()
            .that()
            .areAnnotatedWith(ArchUnitHelper.isEntityAnnotation);

    @Test
    void test_AllEntitiesAreInDataModelPackageOnly() {
        // arrange
        final String PACKAGE_DATA_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".data.model..";

        ArchRule archRule = ArchRuleDefinition.noClasses()
                .that()
                .resideOutsideOfPackage(PACKAGE_DATA_MODEL)
                .should()
                .beAnnotatedWith(ArchUnitHelper.isEntityAnnotation);

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

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

        // arrange
        archRule = ArchRuleDefinition.classes()
                .that()
                .resideInAPackage(PACKAGE_DATA_MODEL)
                .and()
                .areNotEnums()
                .and()
                .areNotInnerClasses()
                .should()
                .beAnnotatedWith(ArchUnitHelper.isEntityAnnotation);

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

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

    @Test
    void test_EntitiesAreImplementingIdentifiableAndSerializable() {
        // arrange
        ArchRule archRule = entities
                .should()
                .implement(Identifiable.class)
                .andShould()
                .implement(Serializable.class);

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

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

    @Test
    void test_EntitiesHaveIdFieldUsingRawType() {
        // arrange
        String idField = "id";
        ArchRule archRule = entities
                .should(hasFieldOfType(idField, String.class)) // Entities should use String for Id (not the Id class)
                .andShould(hasFieldAnnotatedWithType(idField, jakarta.persistence.Id.class));

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

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