package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.ayantdroit.LienParente;
import dev.lions.unionflow.server.api.enums.ayantdroit.StatutAyantDroit;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;

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

@DisplayName("AyantDroit")
class AyantDroitTest {

    private static MembreOrganisation newMembreOrganisation() {
        Membre m = new Membre();
        m.setId(UUID.randomUUID());
        Organisation o = new Organisation();
        o.setId(UUID.randomUUID());
        MembreOrganisation mo = new MembreOrganisation();
        mo.setMembre(m);
        mo.setOrganisation(o);
        return mo;
    }

    @Test
    @DisplayName("getters/setters et constructeur")
    void gettersSetters() {
        MembreOrganisation mo = newMembreOrganisation();
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(mo);
        a.setPrenom("Jean");
        a.setNom("Dupont");
        a.setDateNaissance(LocalDate.of(1990, 5, 15));
        a.setLienParente(LienParente.ENFANT);
        a.setNumeroBeneficiaire("BEN-001");
        a.setDateDebutCouverture(LocalDate.of(2024, 1, 1));
        a.setDateFinCouverture(LocalDate.of(2024, 12, 31));
        a.setSexe("M");
        a.setPieceIdentite("CNI");
        a.setPourcentageCouvertureSante(new BigDecimal("80.00"));
        a.setStatut(StatutAyantDroit.ACTIF);

        assertThat(a.getMembreOrganisation()).isSameAs(mo);
        assertThat(a.getPrenom()).isEqualTo("Jean");
        assertThat(a.getNom()).isEqualTo("Dupont");
        assertThat(a.getDateNaissance()).isEqualTo(LocalDate.of(1990, 5, 15));
        assertThat(a.getLienParente()).isEqualTo(LienParente.ENFANT);
        assertThat(a.getNumeroBeneficiaire()).isEqualTo("BEN-001");
        assertThat(a.getDateDebutCouverture()).isEqualTo(LocalDate.of(2024, 1, 1));
        assertThat(a.getDateFinCouverture()).isEqualTo(LocalDate.of(2024, 12, 31));
        assertThat(a.getSexe()).isEqualTo("M");
        assertThat(a.getPieceIdentite()).isEqualTo("CNI");
        assertThat(a.getPourcentageCouvertureSante()).isEqualByComparingTo("80.00");
        assertThat(a.getStatut()).isEqualTo(StatutAyantDroit.ACTIF);
    }

    @Test
    @DisplayName("statut par défaut EN_ATTENTE")
    void statutDefaut() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.CONJOINT);
        assertThat(a.getStatut()).isEqualTo(StatutAyantDroit.EN_ATTENTE);
    }

    @Test
    @DisplayName("getNomComplet")
    void getNomComplet() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("Marie");
        a.setNom("Martin");
        a.setLienParente(LienParente.ENFANT);
        assertThat(a.getNomComplet()).isEqualTo("Marie Martin");
    }

    @Test
    @DisplayName("isCouvertAujourdhui: false si dateDebut dans le futur")
    void isCouvertAujourdhui_false_debutFutur() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(LocalDate.now().plusDays(10));
        a.setActif(true);
        assertThat(a.isCouvertAujourdhui()).isFalse();
    }

    @Test
    @DisplayName("isCouvertAujourdhui: false si dateFin dans le passé")
    void isCouvertAujourdhui_false_finPassee() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(LocalDate.now().minusDays(10));
        a.setDateFinCouverture(LocalDate.now().minusDays(1));
        a.setActif(true);
        assertThat(a.isCouvertAujourdhui()).isFalse();
    }

    @Test
    @DisplayName("isCouvertAujourdhui: false si actif=false même avec dates valides")
    void isCouvertAujourdhui_false_whenActifFalse() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(LocalDate.now().minusDays(1));
        a.setDateFinCouverture(null);
        a.setActif(false);
        assertThat(a.isCouvertAujourdhui()).isFalse();
    }

    @Test
    @DisplayName("isCouvertAujourdhui: true si actif et dates couvrent aujourd'hui")
    void isCouvertAujourdhui_true() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(LocalDate.now().minusDays(1));
        a.setDateFinCouverture(null);
        a.setActif(true);
        assertThat(a.isCouvertAujourdhui()).isTrue();
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        MembreOrganisation mo = newMembreOrganisation();
        AyantDroit a = new AyantDroit();
        a.setId(id);
        a.setMembreOrganisation(mo);
        a.setPrenom("A");
        a.setNom("B");
        a.setLienParente(LienParente.ENFANT);
        AyantDroit b = new AyantDroit();
        b.setId(id);
        b.setMembreOrganisation(mo);
        b.setPrenom("A");
        b.setNom("B");
        b.setLienParente(LienParente.ENFANT);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        assertThat(a.toString()).isNotNull().isNotEmpty();
    }

    // ── Branch coverage manquantes ─────────────────────────────────────────

    @Test
    @DisplayName("isCouvertAujourdhui: dateDebutCouverture null → condition L85 null short-circuit → continue (branche dateDebutCouverture==null)")
    void isCouvertAujourdhui_debutNull_branchNullShortCircuit() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(null); // null → dateDebutCouverture != null = false → skip at L85
        a.setDateFinCouverture(null);   // null → dateFinCouverture != null = false → skip at L87
        a.setActif(true);
        // Pas de retour false aux conditions → return Boolean.TRUE.equals(true) = true
        assertThat(a.isCouvertAujourdhui()).isTrue();
    }

    /**
     * L85 branch manquante : dateDebutCouverture != null mais today >= dateDebutCouverture
     * (today is NOT before → condition false)
     * → couvre la branche `dateDebutCouverture != null && today.isBefore(...) → false`
     */
    @Test
    @DisplayName("isCouvertAujourdhui: dateDebutCouverture non null mais pas avant → continue (branche false)")
    void isCouvertAujourdhui_debutNonNull_nonBefore_continueToActif() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        // dateDebutCouverture est dans le passé → today.isBefore(debutCouverture) est false
        a.setDateDebutCouverture(LocalDate.now().minusDays(5));
        a.setDateFinCouverture(null);
        a.setActif(true);
        // Ne retourne pas false à la première condition → continue et retourne true (actif=true)
        assertThat(a.isCouvertAujourdhui()).isTrue();
    }

    /**
     * L87 branch manquante : dateFinCouverture != null mais today <= dateFinCouverture
     * (today is NOT after → condition false)
     * → couvre la branche `dateFinCouverture != null && today.isAfter(...) → false`
     */
    @Test
    @DisplayName("isCouvertAujourdhui: dateFinCouverture non null mais pas encore dépassée → continue (branche false)")
    void isCouvertAujourdhui_finNonNull_nonAfter_continueToActif() {
        AyantDroit a = new AyantDroit();
        a.setMembreOrganisation(newMembreOrganisation());
        a.setPrenom("X");
        a.setNom("Y");
        a.setLienParente(LienParente.ENFANT);
        a.setDateDebutCouverture(LocalDate.now().minusDays(5));
        // dateFinCouverture dans le futur → today.isAfter(finCouverture) est false → continue
        a.setDateFinCouverture(LocalDate.now().plusDays(5));
        a.setActif(true);
        assertThat(a.isCouvertAujourdhui()).isTrue();
    }
}
