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

    @Test
    void newTarotSessionCriteriaHasAllFiltersNullTest() {
        var tarotSessionCriteria = new TarotSessionCriteria();
        assertThat(tarotSessionCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void tarotSessionCriteriaFluentMethodsCreatesFiltersTest() {
        var tarotSessionCriteria = new TarotSessionCriteria();

        setAllFilters(tarotSessionCriteria);

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

    @Test
    void tarotSessionCriteriaCopyCreatesNullFilterTest() {
        var tarotSessionCriteria = new TarotSessionCriteria();
        var copy = tarotSessionCriteria.copy();

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

    @Test
    void tarotSessionCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var tarotSessionCriteria = new TarotSessionCriteria();
        setAllFilters(tarotSessionCriteria);

        var copy = tarotSessionCriteria.copy();

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

    @Test
    void toStringVerifier() {
        var tarotSessionCriteria = new TarotSessionCriteria();

        assertThat(tarotSessionCriteria).hasToString("TarotSessionCriteria{}");
    }

    private static void setAllFilters(TarotSessionCriteria tarotSessionCriteria) {
        tarotSessionCriteria.id();
        tarotSessionCriteria.status();
        tarotSessionCriteria.feedback();
        tarotSessionCriteria.rating();
        tarotSessionCriteria.saved();
        tarotSessionCriteria.screenshotUrl();
        tarotSessionCriteria.completedAt();
        tarotSessionCriteria.createdBy();
        tarotSessionCriteria.createdDate();
        tarotSessionCriteria.lastModifiedBy();
        tarotSessionCriteria.lastModifiedDate();
        tarotSessionCriteria.chatMessageId();
        tarotSessionCriteria.userProfileId();
        tarotSessionCriteria.distinct();
    }

    private static Condition<TarotSessionCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getStatus()) &&
                condition.apply(criteria.getFeedback()) &&
                condition.apply(criteria.getRating()) &&
                condition.apply(criteria.getSaved()) &&
                condition.apply(criteria.getScreenshotUrl()) &&
                condition.apply(criteria.getCompletedAt()) &&
                condition.apply(criteria.getCreatedBy()) &&
                condition.apply(criteria.getCreatedDate()) &&
                condition.apply(criteria.getLastModifiedBy()) &&
                condition.apply(criteria.getLastModifiedDate()) &&
                condition.apply(criteria.getChatMessageId()) &&
                condition.apply(criteria.getUserProfileId()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<TarotSessionCriteria> copyFiltersAre(
        TarotSessionCriteria copy,
        BiFunction<Object, Object, Boolean> condition
    ) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getStatus(), copy.getStatus()) &&
                condition.apply(criteria.getFeedback(), copy.getFeedback()) &&
                condition.apply(criteria.getRating(), copy.getRating()) &&
                condition.apply(criteria.getSaved(), copy.getSaved()) &&
                condition.apply(criteria.getScreenshotUrl(), copy.getScreenshotUrl()) &&
                condition.apply(criteria.getCompletedAt(), copy.getCompletedAt()) &&
                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.getChatMessageId(), copy.getChatMessageId()) &&
                condition.apply(criteria.getUserProfileId(), copy.getUserProfileId()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
