package dev.lions.unionflow.server.repository;

import dev.lions.unionflow.server.entity.SystemConfigPersistence;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;

@DisplayName("SystemConfigPersistenceRepository — tests unitaires")
class SystemConfigPersistenceRepositoryTest {

    static class TestableSystemConfigPersistenceRepository extends SystemConfigPersistenceRepository {
        TestableSystemConfigPersistenceRepository(EntityManager em) {
            super();
            this.entityManager = em;
        }
    }

    private EntityManager em;
    private TestableSystemConfigPersistenceRepository repo;

    @BeforeEach
    void setUp() {
        em = mock(EntityManager.class);
        repo = spy(new TestableSystemConfigPersistenceRepository(em));
    }

    @Test
    @DisplayName("constructeur : initialise entityClass à SystemConfigPersistence")
    void constructor_setsEntityClass() {
        assertThat(repo.entityClass).isEqualTo(SystemConfigPersistence.class);
    }

    // ─── findByKey ────────────────────────────────────────────────────────────

    @Test
    @DisplayName("findByKey : retourne Optional.empty() si la clé est absente")
    @SuppressWarnings("unchecked")
    void findByKey_noResult_returnsEmpty() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), anyString());

        Optional<SystemConfigPersistence> result = repo.findByKey("maintenance_mode");

        assertThat(result).isEmpty();
    }

    @Test
    @DisplayName("findByKey : retourne la config si la clé existe")
    @SuppressWarnings("unchecked")
    void findByKey_withResult_returnsConfig() {
        SystemConfigPersistence config = SystemConfigPersistence.builder()
                .configKey("maintenance_mode")
                .configValue("false")
                .build();
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(config));
        doReturn(query).when(repo).find(anyString(), anyString());

        Optional<SystemConfigPersistence> result = repo.findByKey("maintenance_mode");

        assertThat(result).isPresent();
        assertThat(result.get().getConfigKey()).isEqualTo("maintenance_mode");
        assertThat(result.get().getConfigValue()).isEqualTo("false");
    }

    // ─── getValue ─────────────────────────────────────────────────────────────

    @Test
    @DisplayName("getValue : retourne la valeur si la clé existe")
    @SuppressWarnings("unchecked")
    void getValue_keyExists_returnsValue() {
        SystemConfigPersistence config = SystemConfigPersistence.builder()
                .configKey("max_upload_size")
                .configValue("5242880")
                .build();
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(config));
        doReturn(query).when(repo).find(anyString(), anyString());

        String value = repo.getValue("max_upload_size", "1048576");

        assertThat(value).isEqualTo("5242880");
    }

    @Test
    @DisplayName("getValue : retourne la valeur par défaut si la clé est absente")
    @SuppressWarnings("unchecked")
    void getValue_keyAbsent_returnsDefault() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), anyString());

        String value = repo.getValue("unknown_key", "DEFAULT_VALUE");

        assertThat(value).isEqualTo("DEFAULT_VALUE");
    }

    // ─── getBooleanValue ──────────────────────────────────────────────────────

    @Test
    @DisplayName("getBooleanValue : retourne true si valeur est 'true'")
    @SuppressWarnings("unchecked")
    void getBooleanValue_trueValue_returnsTrue() {
        SystemConfigPersistence config = SystemConfigPersistence.builder()
                .configKey("maintenance_mode")
                .configValue("true")
                .build();
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(config));
        doReturn(query).when(repo).find(anyString(), anyString());

        boolean result = repo.getBooleanValue("maintenance_mode", false);

        assertThat(result).isTrue();
    }

    @Test
    @DisplayName("getBooleanValue : retourne false si valeur est 'false'")
    @SuppressWarnings("unchecked")
    void getBooleanValue_falseValue_returnsFalse() {
        SystemConfigPersistence config = SystemConfigPersistence.builder()
                .configKey("maintenance_mode")
                .configValue("false")
                .build();
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(config));
        doReturn(query).when(repo).find(anyString(), anyString());

        boolean result = repo.getBooleanValue("maintenance_mode", true);

        assertThat(result).isFalse();
    }

    @Test
    @DisplayName("getBooleanValue : retourne la valeur par défaut si clé absente")
    @SuppressWarnings("unchecked")
    void getBooleanValue_keyAbsent_returnsDefault() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), anyString());

        boolean result = repo.getBooleanValue("missing_key", true);

        assertThat(result).isTrue();
    }

    @Test
    @DisplayName("getBooleanValue : valeur par défaut false si clé absente")
    @SuppressWarnings("unchecked")
    void getBooleanValue_keyAbsent_defaultFalse() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), anyString());

        boolean result = repo.getBooleanValue("missing_key", false);

        assertThat(result).isFalse();
    }

    // ─── setValue ─────────────────────────────────────────────────────────────

    @Test
    @DisplayName("setValue : met à jour la valeur si la clé existe déjà")
    @SuppressWarnings("unchecked")
    void setValue_existingKey_updatesValue() {
        SystemConfigPersistence existing = SystemConfigPersistence.builder()
                .configKey("maintenance_mode")
                .configValue("false")
                .build();
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.of(existing));
        doReturn(query).when(repo).find(anyString(), anyString());
        doNothing().when(repo).persist(any(SystemConfigPersistence.class));

        repo.setValue("maintenance_mode", "true");

        assertThat(existing.getConfigValue()).isEqualTo("true");
        verify(repo).persist(existing);
    }

    @Test
    @DisplayName("setValue : crée une nouvelle entrée si la clé est absente")
    @SuppressWarnings("unchecked")
    void setValue_newKey_persistsNewConfig() {
        io.quarkus.hibernate.orm.panache.PanacheQuery<SystemConfigPersistence> query =
                mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
        when(query.firstResultOptional()).thenReturn(Optional.empty());
        doReturn(query).when(repo).find(anyString(), anyString());
        doNothing().when(repo).persist(any(SystemConfigPersistence.class));

        repo.setValue("new_feature_flag", "enabled");

        verify(repo).persist(any(SystemConfigPersistence.class));
    }
}
