import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.JavaModifier;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ConditionEvents;
import com.tngtech.archunit.lang.SimpleConditionEvent;

public class CustomArchConditions {

    private static final String EQUALS_AND_HASH_CODE_DESCRIPTION = "have equals and hashCode";
    private static final String EQUALS_OR_HASH_CODE_NOT_PRESENT_ERROR_MESSAGE = "%s not found in %s";
    private static final String EQUALS_METHOD = "equals";
    private static final String HASH_CODE_METHOD = "hashCode";

    public static final ArchCondition<JavaClass> HAVE_EQUALS_AND_HASH_CODE = buildClassHaveEqualsAndHashCodeCondition();

    private CustomArchConditions() {

    }

    private static ArchCondition<JavaClass> buildClassHaveEqualsAndHashCodeCondition() {
        return new ArchCondition<JavaClass>(EQUALS_AND_HASH_CODE_DESCRIPTION) {

            @Override
            public void check(JavaClass javaClass, ConditionEvents events) {
                Optional<JavaMethod> equalsMethod = findPublicMethodFromClass(javaClass, EQUALS_METHOD);
                Optional<JavaMethod> hashCodeMethod = findPublicMethodFromClass(javaClass, HASH_CODE_METHOD);

                if (!equalsMethod.isPresent()) {
                    events.add(SimpleConditionEvent.violated(javaClass,
                            String.format(EQUALS_OR_HASH_CODE_NOT_PRESENT_ERROR_MESSAGE, EQUALS_METHOD,
                                    javaClass.getName())));
                }

                if (!hashCodeMethod.isPresent()) {
                    events.add(SimpleConditionEvent.violated(javaClass,
                            String.format(EQUALS_OR_HASH_CODE_NOT_PRESENT_ERROR_MESSAGE, HASH_CODE_METHOD,
                                    javaClass.getName())));
                }
            }
        };
    }


    private static Optional<JavaMethod> findPublicMethodFromClass(JavaClass javaClass, String methodName) {
        return javaClass.getMethods().stream()
                .filter(m -> m.getModifiers().contains(JavaModifier.PUBLIC) && methodName.equals(m.getName()))
                .findFirst();
    }
}