package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.abonnement.StatutSouscription;
import dev.lions.unionflow.server.api.enums.abonnement.PlageMembres;
import dev.lions.unionflow.server.api.enums.abonnement.TypeFormule;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@DisplayName("SouscriptionOrganisation")
class SouscriptionOrganisationTest {

    private static Organisation newOrganisation() {
        Organisation o = new Organisation();
        o.setId(UUID.randomUUID());
        o.setNom("Org");
        o.setTypeOrganisation("X");
        o.setStatut("ACTIVE");
        o.setEmail("org@test.com");
        return o;
    }

    private static FormuleAbonnement newFormule() {
        FormuleAbonnement f = new FormuleAbonnement();
        f.setId(UUID.randomUUID());
        f.setCode(TypeFormule.BASIC);
        f.setPlage(PlageMembres.PETITE);
        f.setLibelle("Basic");
        f.setMaxMembres(100);
        f.setPrixMensuel(java.math.BigDecimal.valueOf(5000));
        f.setPrixAnnuel(java.math.BigDecimal.valueOf(50000));
        return f;
    }

    @Test
    @DisplayName("getters/setters")
    void gettersSetters() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setTypePeriode(TypePeriodeAbonnement.MENSUEL);
        s.setDateDebut(LocalDate.now());
        s.setDateFin(LocalDate.now().plusYears(1));
        s.setQuotaMax(50);
        s.setQuotaUtilise(10);
        s.setStatut(StatutSouscription.ACTIVE);

        assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
        assertThat(s.getQuotaMax()).isEqualTo(50);
        assertThat(s.getQuotaUtilise()).isEqualTo(10);
    }

    @Test
    @DisplayName("isActive")
    void isActive() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setDateDebut(LocalDate.now().minusMonths(1));
        s.setDateFin(LocalDate.now().plusMonths(1));
        s.setStatut(StatutSouscription.ACTIVE);
        assertThat(s.isActive()).isTrue();
        s.setDateFin(LocalDate.now().minusDays(1));
        assertThat(s.isActive()).isFalse();
    }

    @Test
    @DisplayName("isQuotaDepasse et getPlacesRestantes")
    void isQuotaDepasse_getPlacesRestantes() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setDateDebut(LocalDate.now());
        s.setDateFin(LocalDate.now().plusYears(1));
        s.setQuotaMax(10);
        s.setQuotaUtilise(10);
        assertThat(s.isQuotaDepasse()).isTrue();
        assertThat(s.getPlacesRestantes()).isEqualTo(0);
        s.setQuotaUtilise(5);
        assertThat(s.isQuotaDepasse()).isFalse();
        assertThat(s.getPlacesRestantes()).isEqualTo(5);
    }

    @Test
    @DisplayName("incrementerQuota et decrementerQuota")
    void incrementerDecrementerQuota() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setQuotaUtilise(5);
        s.incrementerQuota();
        assertThat(s.getQuotaUtilise()).isEqualTo(6);
        s.decrementerQuota();
        s.decrementerQuota();
        assertThat(s.getQuotaUtilise()).isEqualTo(4);
    }

    @Test
    @DisplayName("equals et hashCode")
    void equalsHashCode() {
        UUID id = UUID.randomUUID();
        Organisation o = newOrganisation();
        FormuleAbonnement f = newFormule();
        LocalDate debut = LocalDate.now();
        LocalDate fin = LocalDate.now().plusYears(1);
        SouscriptionOrganisation a = new SouscriptionOrganisation();
        a.setId(id);
        a.setOrganisation(o);
        a.setFormule(f);
        a.setDateDebut(debut);
        a.setDateFin(fin);
        SouscriptionOrganisation b = new SouscriptionOrganisation();
        b.setId(id);
        b.setOrganisation(o);
        b.setFormule(f);
        b.setDateDebut(debut);
        b.setDateFin(fin);
        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
    }

    @Test
    @DisplayName("toString non null")
    void toString_nonNull() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setDateDebut(LocalDate.now());
        s.setDateFin(LocalDate.now().plusYears(1));
        assertThat(s.toString()).isNotNull().isNotEmpty();
    }

    @Test
    @DisplayName("isQuotaDepasse: false si quotaMax null")
    void isQuotaDepasse_quotaMaxNull_returnsFalse() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setQuotaMax(null);
        s.setQuotaUtilise(100);
        assertThat(s.isQuotaDepasse()).isFalse();
    }

    @Test
    @DisplayName("getPlacesRestantes: MAX_VALUE si quotaMax null")
    void getPlacesRestantes_quotaMaxNull_returnsMaxInt() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setQuotaMax(null);
        assertThat(s.getPlacesRestantes()).isEqualTo(Integer.MAX_VALUE);
    }

    @Test
    @DisplayName("incrementerQuota: quotaUtilise null → initialisé à 1")
    void incrementerQuota_nullQuota_initializesTo1() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setQuotaUtilise(null);
        s.incrementerQuota();
        assertThat(s.getQuotaUtilise()).isEqualTo(1);
    }

    @Test
    @DisplayName("decrementerQuota: quotaUtilise=0 → ne descend pas")
    void decrementerQuota_zeroQuota_noChange() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setQuotaUtilise(0);
        s.decrementerQuota();
        assertThat(s.getQuotaUtilise()).isEqualTo(0);
    }

    @Test
    @DisplayName("isActive: false si statut SUSPENDUE")
    void isActive_statusSuspendue_returnsFalse() {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(newFormule());
        s.setDateDebut(LocalDate.now().minusMonths(1));
        s.setDateFin(LocalDate.now().plusMonths(1));
        s.setStatut(StatutSouscription.SUSPENDUE);
        assertThat(s.isActive()).isFalse();
    }

    @Test
    @DisplayName("onCreate: initialise les défauts si null, copie quotaMax depuis formule")
    void onCreate_setsDefaults() throws Exception {
        FormuleAbonnement formule = newFormule(); // maxMembres=100
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(formule);
        s.setDateDebut(LocalDate.now());
        s.setDateFin(LocalDate.now().plusYears(1));
        s.setStatut(null);
        s.setTypePeriode(null);
        s.setQuotaUtilise(null);
        s.setQuotaMax(null);

        Method onCreate = SouscriptionOrganisation.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(s);

        assertThat(s.getStatut()).isEqualTo(StatutSouscription.ACTIVE);
        assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
        assertThat(s.getQuotaUtilise()).isEqualTo(0);
        assertThat(s.getQuotaMax()).isEqualTo(100);
    }

    // ── Branch coverage manquante ──────────────────────────────────────────

    /**
     * L118 branch manquante : formule == null → `if (formule != null && quotaMax == null)` → false (court-circuit)
     * → quotaMax reste null (ou tel quel), pas de NPE
     */
    @Test
    @DisplayName("onCreate: formule == null → la branche quotaMax depuis formule n'est pas exécutée")
    void onCreate_formulesNull_quotaMaxUnchanged() throws Exception {
        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setOrganisation(newOrganisation());
        s.setFormule(null); // formule == null → branche false (court-circuit)
        s.setDateDebut(LocalDate.now());
        s.setDateFin(LocalDate.now().plusYears(1));
        s.setStatut(null);
        s.setTypePeriode(null);
        s.setQuotaUtilise(null);
        s.setQuotaMax(null);

        Method onCreate = SouscriptionOrganisation.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(s);

        // les défauts sont positionnés, mais quotaMax reste null (formule est null)
        assertThat(s.getStatut()).isEqualTo(StatutSouscription.ACTIVE);
        assertThat(s.getTypePeriode()).isEqualTo(TypePeriodeAbonnement.MENSUEL);
        assertThat(s.getQuotaUtilise()).isEqualTo(0);
        assertThat(s.getQuotaMax()).isNull(); // formule null → quotaMax non initialisé
    }
}
