package dev.lions.unionflow.server.entity;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Tests unitaires pour BaseEntity (getters/setters, callbacks @PrePersist/@PreUpdate, marquerCommeModifie).
 * BaseEntity étant abstraite, on s'appuie sur une sous-classe concrète (Adresse).
 */
@DisplayName("BaseEntity")
class BaseEntityTest {

    @Test
    @DisplayName("getters/setters: id, dateCreation, dateModification, creePar, modifiePar, version, actif")
    void gettersSetters() {
        Adresse entity = new Adresse();
        UUID id = UUID.randomUUID();
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime later = now.plusHours(1);

        entity.setId(id);
        entity.setDateCreation(now);
        entity.setDateModification(later);
        entity.setCreePar("createur@test.com");
        entity.setModifiePar("modif@test.com");
        entity.setVersion(1L);
        entity.setActif(true);

        assertThat(entity.getId()).isEqualTo(id);
        assertThat(entity.getDateCreation()).isEqualTo(now);
        assertThat(entity.getDateModification()).isEqualTo(later);
        assertThat(entity.getCreePar()).isEqualTo("createur@test.com");
        assertThat(entity.getModifiePar()).isEqualTo("modif@test.com");
        assertThat(entity.getVersion()).isEqualTo(1L);
        assertThat(entity.getActif()).isTrue();

        entity.setActif(false);
        assertThat(entity.getActif()).isFalse();
    }

    @Test
    @DisplayName("marquerCommeModifie met à jour dateModification et modifiePar")
    void marquerCommeModifie() {
        Adresse entity = new Adresse();
        entity.setDateModification(null);
        entity.setModifiePar(null);

        entity.marquerCommeModifie("user@test.com");

        assertThat(entity.getDateModification()).isNotNull();
        assertThat(entity.getModifiePar()).isEqualTo("user@test.com");
    }

    @Test
    @DisplayName("@PrePersist onCreate: initialise dateCreation si null")
    void onCreate_initialiseDateCreationSiNull() throws Exception {
        Adresse entity = new Adresse();
        entity.setDateCreation(null);
        entity.setActif(null);

        Method onCreate = BaseEntity.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(entity);

        assertThat(entity.getDateCreation()).isNotNull();
        assertThat(entity.getActif()).isTrue();
    }

    @Test
    @DisplayName("@PrePersist onCreate: ne modifie pas dateCreation si déjà renseignée")
    void onCreate_neModifiePasDateCreationSiDejaRenseignee() throws Exception {
        Adresse entity = new Adresse();
        LocalDateTime fixed = LocalDateTime.of(2025, 1, 1, 12, 0);
        entity.setDateCreation(fixed);
        entity.setActif(true);

        Method onCreate = BaseEntity.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(entity);

        assertThat(entity.getDateCreation()).isEqualTo(fixed);
        assertThat(entity.getActif()).isTrue();
    }

    @Test
    @DisplayName("@PrePersist onCreate: initialise actif à true si null")
    void onCreate_initialiseActifSiNull() throws Exception {
        Adresse entity = new Adresse();
        entity.setDateCreation(LocalDateTime.now());
        entity.setActif(null);

        Method onCreate = BaseEntity.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(entity);

        assertThat(entity.getActif()).isTrue();
    }

    @Test
    @DisplayName("@PreUpdate onUpdate: met à jour dateModification")
    void onUpdate_metAJourDateModification() throws Exception {
        Adresse entity = new Adresse();
        entity.setDateModification(null);

        Method onUpdate = BaseEntity.class.getDeclaredMethod("onUpdate");
        onUpdate.setAccessible(true);
        onUpdate.invoke(entity);

        assertThat(entity.getDateModification()).isNotNull();
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        Adresse entity = new Adresse();
        entity.setId(UUID.randomUUID());
        assertThat(entity.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("equals et hashCode cohérents avec champs de base")
    void equalsHashCode() {
        Adresse a = new Adresse();
        a.setId(UUID.randomUUID());
        a.setTypeAdresse("SIEGE");
        Adresse b = new Adresse();
        b.setId(a.getId());
        b.setTypeAdresse("SIEGE");

        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
        assertThat(a).isEqualTo(a);
        assertThat(a).isNotEqualTo(null);
        assertThat(a).isNotEqualTo("autre type");
    }
}
