package com.emonster.taroaichat.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 TarotCardCriteriaTest {

    @Test
    void newTarotCardCriteriaHasAllFiltersNullTest() {
        var tarotCardCriteria = new TarotCardCriteria();
        assertThat(tarotCardCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void tarotCardCriteriaFluentMethodsCreatesFiltersTest() {
        var tarotCardCriteria = new TarotCardCriteria();

        setAllFilters(tarotCardCriteria);

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

    @Test
    void tarotCardCriteriaCopyCreatesNullFilterTest() {
        var tarotCardCriteria = new TarotCardCriteria();
        var copy = tarotCardCriteria.copy();

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

    @Test
    void tarotCardCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var tarotCardCriteria = new TarotCardCriteria();
        setAllFilters(tarotCardCriteria);

        var copy = tarotCardCriteria.copy();

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

    @Test
    void toStringVerifier() {
        var tarotCardCriteria = new TarotCardCriteria();

        assertThat(tarotCardCriteria).hasToString("TarotCardCriteria{}");
    }

    private static void setAllFilters(TarotCardCriteria tarotCardCriteria) {
        tarotCardCriteria.id();
        tarotCardCriteria.name();
        tarotCardCriteria.arcanaType();
        tarotCardCriteria.cardNumber();
        tarotCardCriteria.imageUrl();
        tarotCardCriteria.keywords();
        tarotCardCriteria.createdBy();
        tarotCardCriteria.createdDate();
        tarotCardCriteria.lastModifiedBy();
        tarotCardCriteria.lastModifiedDate();
        tarotCardCriteria.distinct();
    }

    private static Condition<TarotCardCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getName()) &&
                condition.apply(criteria.getArcanaType()) &&
                condition.apply(criteria.getCardNumber()) &&
                condition.apply(criteria.getImageUrl()) &&
                condition.apply(criteria.getKeywords()) &&
                condition.apply(criteria.getCreatedBy()) &&
                condition.apply(criteria.getCreatedDate()) &&
                condition.apply(criteria.getLastModifiedBy()) &&
                condition.apply(criteria.getLastModifiedDate()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<TarotCardCriteria> copyFiltersAre(TarotCardCriteria copy, BiFunction<Object, Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getName(), copy.getName()) &&
                condition.apply(criteria.getArcanaType(), copy.getArcanaType()) &&
                condition.apply(criteria.getCardNumber(), copy.getCardNumber()) &&
                condition.apply(criteria.getImageUrl(), copy.getImageUrl()) &&
                condition.apply(criteria.getKeywords(), copy.getKeywords()) &&
                condition.apply(criteria.getCreatedBy(), copy.getCreatedBy()) &&
                condition.apply(criteria.getCreatedDate(), copy.getCreatedDate()) &&
                condition.apply(criteria.getLastModifiedBy(), copy.getLastModifiedBy()) &&
                condition.apply(criteria.getLastModifiedDate(), copy.getLastModifiedDate()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
