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.common.shared_kernel.Identifiable;
import de.accso.flexinale.common.shared_kernel.Versionable;
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_NoEntitiesAreOutsidePersistencePackages() {
        // arrange
        final String PACKAGE_BACKOFFICE_INFRASTRUCTURE_PERSISTENCE = ArchUnitHelper.PACKAGE_PREFIX + ".backoffice.infrastructure.persistence..";
        final String PACKAGE_SECURITY_INFRASTRUCTURE_PERSISTENCE   = ArchUnitHelper.PACKAGE_PREFIX + ".security.infrastructure.persistence..";
        final String PACKAGE_TICKETING_INFRASTRUCTURE_PERSISTENCE  = ArchUnitHelper.PACKAGE_PREFIX + ".ticketing.infrastructure.persistence..";

        ArchRule archRule = ArchRuleDefinition.noClasses()
                .that()
                .resideOutsideOfPackages(PACKAGE_BACKOFFICE_INFRASTRUCTURE_PERSISTENCE,
                        PACKAGE_SECURITY_INFRASTRUCTURE_PERSISTENCE,
                        PACKAGE_TICKETING_INFRASTRUCTURE_PERSISTENCE)
                .should()
                .beAnnotatedWith(ArchUnitHelper.isEntityAnnotation);

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

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

    @Test
    void test_EntitiesAreImplementingIdentifiableAndSerializableAndVersionable() {
        // arrange
        ArchRule archRule = entities
                .should()
                .implement(Identifiable.class)
                .andShould()
                .implement(Serializable.class)
                .andShould()
                .implement(Versionable.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();
    }

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

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

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

    @Test
    void test_EntitiesHaveEntityAsSuffixClassName() {
        // arrange
        String clazzNameSuffix = "Entity";

        ArchRule archRule = entities
                .should()
                .haveSimpleNameEndingWith(clazzNameSuffix);

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

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