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

    @Test
    void newChatMessageCriteriaHasAllFiltersNullTest() {
        var chatMessageCriteria = new ChatMessageCriteria();
        assertThat(chatMessageCriteria).is(criteriaFiltersAre(Objects::isNull));
    }

    @Test
    void chatMessageCriteriaFluentMethodsCreatesFiltersTest() {
        var chatMessageCriteria = new ChatMessageCriteria();

        setAllFilters(chatMessageCriteria);

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

    @Test
    void chatMessageCriteriaCopyCreatesNullFilterTest() {
        var chatMessageCriteria = new ChatMessageCriteria();
        var copy = chatMessageCriteria.copy();

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

    @Test
    void chatMessageCriteriaCopyDuplicatesEveryExistingFilterTest() {
        var chatMessageCriteria = new ChatMessageCriteria();
        setAllFilters(chatMessageCriteria);

        var copy = chatMessageCriteria.copy();

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

    @Test
    void toStringVerifier() {
        var chatMessageCriteria = new ChatMessageCriteria();

        assertThat(chatMessageCriteria).hasToString("ChatMessageCriteria{}");
    }

    private static void setAllFilters(ChatMessageCriteria chatMessageCriteria) {
        chatMessageCriteria.id();
        chatMessageCriteria.sender();
        chatMessageCriteria.timestamp();
        chatMessageCriteria.createdBy();
        chatMessageCriteria.createdDate();
        chatMessageCriteria.lastModifiedBy();
        chatMessageCriteria.lastModifiedDate();
        chatMessageCriteria.sessionId();
        chatMessageCriteria.distinct();
    }

    private static Condition<ChatMessageCriteria> criteriaFiltersAre(Function<Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId()) &&
                condition.apply(criteria.getSender()) &&
                condition.apply(criteria.getTimestamp()) &&
                condition.apply(criteria.getCreatedBy()) &&
                condition.apply(criteria.getCreatedDate()) &&
                condition.apply(criteria.getLastModifiedBy()) &&
                condition.apply(criteria.getLastModifiedDate()) &&
                condition.apply(criteria.getSessionId()) &&
                condition.apply(criteria.getDistinct()),
            "every filter matches"
        );
    }

    private static Condition<ChatMessageCriteria> copyFiltersAre(ChatMessageCriteria copy, BiFunction<Object, Object, Boolean> condition) {
        return new Condition<>(
            criteria ->
                condition.apply(criteria.getId(), copy.getId()) &&
                condition.apply(criteria.getSender(), copy.getSender()) &&
                condition.apply(criteria.getTimestamp(), copy.getTimestamp()) &&
                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.getSessionId(), copy.getSessionId()) &&
                condition.apply(criteria.getDistinct(), copy.getDistinct()),
            "every filter matches"
        );
    }
}
