package it.sw.pa.comune.artegna.service.criteria;

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

import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.Test;

class NotificaCriteriaTest {

    @Test
    void newNotificaCriteriaHasAllFiltersNullTest() {
        var notificaCriteria = new NotificaCriteria();
        assertThat(notificaCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void notificaCriteriaFluentMethodsCreatesFiltersTest() {
        var notificaCriteria = new NotificaCriteria();

        setAllFilters(notificaCriteria);

        assertThat(notificaCriteria).is(criteriaFiltersAre(Objects::nonNull));
    }

    @Test
    void notificaCriteriaCopyCreatesNullFilterTest() {
        var notificaCriteria = new NotificaCriteria();
        var copy = notificaCriteria.copy();

        assertThat(notificaCriteria).satisfies(
            criteria ->
                assertThat(criteria).is(
                    copyFiltersAre(copy, (a, b) -> (a == null || a instanceof Boolean) ? a == b : (a != b && a.equals(b)))
                ),
            criteria -> assertThat(criteria).isEqualTo(copy),
            criteria -> assertThat(criteria).hasSameHashCodeAs(copy)
        );

        assertThat(copy).satisfies(
            criteria -> assertThat(criteria).is(criteriaFiltersAre(Objects::isNull)),
            criteria -> assertThat(criteria).isEqualTo(notificaCriteria)
        );
    }

    @Test
    void notificaCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var notificaCriteria = new NotificaCriteria();
        setAllFilters(notificaCriteria);

        var copy = notificaCriteria.copy();

        assertThat(notificaCriteria).satisfies(
            criteria ->
                assertThat(criteria).is(
                    copyFiltersAre(copy, (a, b) -> (a == null || a instanceof Boolean) ? a == b : (a != b && a.equals(b)))
                ),
            criteria -> assertThat(criteria).isEqualTo(copy),
            criteria -> assertThat(criteria).hasSameHashCodeAs(copy)
        );

        assertThat(copy).satisfies(
            criteria -> assertThat(criteria).is(criteriaFiltersAre(Objects::nonNull)),
            criteria -> assertThat(criteria).isEqualTo(notificaCriteria)
        );
    }

    @Test
    void toStringVerifier() {
        var notificaCriteria = new NotificaCriteria();

        assertThat(notificaCriteria).hasToString("NotificaCriteria{}");
    }

    private static void setAllFilters(NotificaCriteria notificaCriteria) {
        notificaCriteria.id();
        notificaCriteria.tipoCanale();
        notificaCriteria.tipoEvento();
        notificaCriteria.messaggio();
        notificaCriteria.inviata();
        notificaCriteria.inviataAt();
        notificaCriteria.errore();
        notificaCriteria.createdAt();
        notificaCriteria.confermaId();
        notificaCriteria.distinct();
    }

    private static Condition<NotificaCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getTipoCanale()) &&
                condition.apply(criteria.getTipoEvento()) &&
                condition.apply(criteria.getMessaggio()) &&
                condition.apply(criteria.getInviata()) &&
                condition.apply(criteria.getInviataAt()) &&
                condition.apply(criteria.getErrore()) &&
                condition.apply(criteria.getCreatedAt()) &&
                condition.apply(criteria.getConfermaId()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<NotificaCriteria> copyFiltersAre(NotificaCriteria copy, BiFunction<Object, Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getTipoCanale(), copy.getTipoCanale()) &&
                condition.apply(criteria.getTipoEvento(), copy.getTipoEvento()) &&
                condition.apply(criteria.getMessaggio(), copy.getMessaggio()) &&
                condition.apply(criteria.getInviata(), copy.getInviata()) &&
                condition.apply(criteria.getInviataAt(), copy.getInviataAt()) &&
                condition.apply(criteria.getErrore(), copy.getErrore()) &&
                condition.apply(criteria.getCreatedAt(), copy.getCreatedAt()) &&
                condition.apply(criteria.getConfermaId(), copy.getConfermaId()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
