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

    @Test
    void newPrenotazioneCriteriaHasAllFiltersNullTest() {
        var prenotazioneCriteria = new PrenotazioneCriteria();
        assertThat(prenotazioneCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void prenotazioneCriteriaFluentMethodsCreatesFiltersTest() {
        var prenotazioneCriteria = new PrenotazioneCriteria();

        setAllFilters(prenotazioneCriteria);

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

    @Test
    void prenotazioneCriteriaCopyCreatesNullFilterTest() {
        var prenotazioneCriteria = new PrenotazioneCriteria();
        var copy = prenotazioneCriteria.copy();

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

    @Test
    void prenotazioneCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var prenotazioneCriteria = new PrenotazioneCriteria();
        setAllFilters(prenotazioneCriteria);

        var copy = prenotazioneCriteria.copy();

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

    @Test
    void toStringVerifier() {
        var prenotazioneCriteria = new PrenotazioneCriteria();

        assertThat(prenotazioneCriteria).hasToString("PrenotazioneCriteria{}");
    }

    private static void setAllFilters(PrenotazioneCriteria prenotazioneCriteria) {
        prenotazioneCriteria.id();
        prenotazioneCriteria.oraInizio();
        prenotazioneCriteria.oraFine();
        prenotazioneCriteria.stato();
        prenotazioneCriteria.motivoEvento();
        prenotazioneCriteria.numeroPartecipanti();
        prenotazioneCriteria.noteUtente();
        prenotazioneCriteria.confermaId();
        prenotazioneCriteria.utenteId();
        prenotazioneCriteria.strutturaId();
        prenotazioneCriteria.distinct();
    }

    private static Condition<PrenotazioneCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getOraInizio()) &&
                condition.apply(criteria.getOraFine()) &&
                condition.apply(criteria.getStato()) &&
                condition.apply(criteria.getMotivoEvento()) &&
                condition.apply(criteria.getNumeroPartecipanti()) &&
                condition.apply(criteria.getNoteUtente()) &&
                condition.apply(criteria.getConfermaId()) &&
                condition.apply(criteria.getUtenteId()) &&
                condition.apply(criteria.getStrutturaId()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<PrenotazioneCriteria> copyFiltersAre(
        PrenotazioneCriteria copy,
        BiFunction<Object, Object, Boolean> condition
    ) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getOraInizio(), copy.getOraInizio()) &&
                condition.apply(criteria.getOraFine(), copy.getOraFine()) &&
                condition.apply(criteria.getStato(), copy.getStato()) &&
                condition.apply(criteria.getMotivoEvento(), copy.getMotivoEvento()) &&
                condition.apply(criteria.getNumeroPartecipanti(), copy.getNumeroPartecipanti()) &&
                condition.apply(criteria.getNoteUtente(), copy.getNoteUtente()) &&
                condition.apply(criteria.getConfermaId(), copy.getConfermaId()) &&
                condition.apply(criteria.getUtenteId(), copy.getUtenteId()) &&
                condition.apply(criteria.getStrutturaId(), copy.getStrutturaId()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
