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 ConfermaCriteriaTest {

    @Test
    void newConfermaCriteriaHasAllFiltersNullTest() {
        var confermaCriteria = new ConfermaCriteria();
        assertThat(confermaCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void confermaCriteriaFluentMethodsCreatesFiltersTest() {
        var confermaCriteria = new ConfermaCriteria();

        setAllFilters(confermaCriteria);

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

    @Test
    void confermaCriteriaCopyCreatesNullFilterTest() {
        var confermaCriteria = new ConfermaCriteria();
        var copy = confermaCriteria.copy();

        assertThat(confermaCriteria).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(confermaCriteria)
        );
    }

    @Test
    void confermaCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var confermaCriteria = new ConfermaCriteria();
        setAllFilters(confermaCriteria);

        var copy = confermaCriteria.copy();

        assertThat(confermaCriteria).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(confermaCriteria)
        );
    }

    @Test
    void toStringVerifier() {
        var confermaCriteria = new ConfermaCriteria();

        assertThat(confermaCriteria).hasToString("ConfermaCriteria{}");
    }

    private static void setAllFilters(ConfermaCriteria confermaCriteria) {
        confermaCriteria.id();
        confermaCriteria.motivoConferma();
        confermaCriteria.tipoConferma();
        confermaCriteria.confermataDaId();
        confermaCriteria.prenotazioneId();
        confermaCriteria.distinct();
    }

    private static Condition<ConfermaCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getMotivoConferma()) &&
                condition.apply(criteria.getTipoConferma()) &&
                condition.apply(criteria.getConfermataDaId()) &&
                condition.apply(criteria.getPrenotazioneId()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<ConfermaCriteria> copyFiltersAre(ConfermaCriteria copy, BiFunction<Object, Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getMotivoConferma(), copy.getMotivoConferma()) &&
                condition.apply(criteria.getTipoConferma(), copy.getTipoConferma()) &&
                condition.apply(criteria.getConfermataDaId(), copy.getConfermataDaId()) &&
                condition.apply(criteria.getPrenotazioneId(), copy.getPrenotazioneId()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
