package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.abonnement.PlageMembres;
import dev.lions.unionflow.server.api.enums.abonnement.StatutSouscription;
import dev.lions.unionflow.server.api.enums.abonnement.TypeFormule;
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;

/**
 * Tests unitaires pour la logique de quota (Option C) et les champs de plan commercial.
 *
 * <p>Couvre :
 * <ul>
 *   <li>{@link SouscriptionOrganisation#isActive()} — selon statut + dateFin</li>
 *   <li>{@link SouscriptionOrganisation#isQuotaDepasse()} — selon quotaMax et quotaUtilise</li>
 *   <li>{@link SouscriptionOrganisation#getPlacesRestantes()} — calcul des places libres</li>
 *   <li>{@link FormuleAbonnement#isIllimitee()} — maxMembres null = illimité</li>
 *   <li>{@link FormuleAbonnement#accepteNouveauMembre(int)} — contrôle admission</li>
 *   <li>Champs Option C (planCommercial, apiAccess, federationAccess, maxAdmins)</li>
 * </ul>
 */
@DisplayName("SouscriptionOrganisation + FormuleAbonnement — Quota & Option C")
class SouscriptionQuotaOptionCTest {

    // ─── Builders ─────────────────────────────────────────────────────────────

    private static Organisation newOrganisation() {
        Organisation o = new Organisation();
        o.setId(UUID.randomUUID());
        o.setNom("Association Test");
        o.setTypeOrganisation("ASSOCIATION");
        o.setStatut("ACTIVE");
        o.setEmail("test@asso.sn");
        return o;
    }

    private static FormuleAbonnement newFormule(int maxMembres) {
        FormuleAbonnement f = new FormuleAbonnement();
        f.setId(UUID.randomUUID());
        f.setCode(TypeFormule.STANDARD);
        f.setPlage(PlageMembres.PETITE);
        f.setLibelle("Standard Petite");
        f.setMaxMembres(maxMembres);
        f.setPrixMensuel(BigDecimal.valueOf(7500));
        f.setPrixAnnuel(BigDecimal.valueOf(75000));
        return f;
    }

    private static FormuleAbonnement newFormuleIllimitee() {
        FormuleAbonnement f = new FormuleAbonnement();
        f.setId(UUID.randomUUID());
        f.setCode(TypeFormule.PREMIUM);
        f.setPlage(PlageMembres.GRANDE);
        f.setLibelle("Enterprise");
        f.setMaxMembres(null); // illimité
        f.setPrixMensuel(BigDecimal.valueOf(30000));
        f.setPrixAnnuel(BigDecimal.valueOf(300000));
        return f;
    }

    private static SouscriptionOrganisation newSouscription(
            Organisation org, FormuleAbonnement formule,
            StatutSouscription statut, LocalDate dateFin,
            int quotaMax, int quotaUtilise) {

        SouscriptionOrganisation s = new SouscriptionOrganisation();
        s.setId(UUID.randomUUID());
        s.setOrganisation(org);
        s.setFormule(formule);
        s.setStatut(statut);
        s.setDateDebut(LocalDate.now().minusMonths(1));
        s.setDateFin(dateFin);
        s.setQuotaMax(quotaMax);
        s.setQuotaUtilise(quotaUtilise);
        return s;
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  isActive()
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("isActive: statut ACTIVE et dateFin future → true")
    void isActive_activePlusFutureDateFin_returnsTrue() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(6), 100, 0);

        assertThat(s.isActive()).isTrue();
    }

    @Test
    @DisplayName("isActive: statut ACTIVE mais dateFin hier → false (expirée)")
    void isActive_activePlusExpiredDate_returnsFalse() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().minusDays(1), 100, 0);

        assertThat(s.isActive()).isFalse();
    }

    @Test
    @DisplayName("isActive: statut SUSPENDUE → false")
    void isActive_suspendue_returnsFalse() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.SUSPENDUE, LocalDate.now().plusMonths(1), 100, 0);

        assertThat(s.isActive()).isFalse();
    }

    @Test
    @DisplayName("isActive: statut EXPIREE → false")
    void isActive_expiree_returnsFalse() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.EXPIREE, LocalDate.now().plusMonths(1), 100, 0);

        assertThat(s.isActive()).isFalse();
    }

    @Test
    @DisplayName("isActive: dateFin aujourd'hui → true (borne inclusive)")
    void isActive_dateFin_today_returnsTrue() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now(), 100, 0);

        assertThat(s.isActive()).isTrue();
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  isQuotaDepasse()
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("isQuotaDepasse: quotaUtilise < quotaMax → false")
    void isQuotaDepasse_utiliseInferieur_returnsFalse() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 50);

        assertThat(s.isQuotaDepasse()).isFalse();
    }

    @Test
    @DisplayName("isQuotaDepasse: quotaUtilise == quotaMax → true (atteint)")
    void isQuotaDepasse_utiliseEgalMax_returnsTrue() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 100);

        assertThat(s.isQuotaDepasse()).isTrue();
    }

    @Test
    @DisplayName("isQuotaDepasse: quotaUtilise > quotaMax → true (dépassé)")
    void isQuotaDepasse_utiliseSuperieur_returnsTrue() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 110);

        assertThat(s.isQuotaDepasse()).isTrue();
    }

    @Test
    @DisplayName("isQuotaDepasse: quotaMax null (illimité) → false")
    void isQuotaDepasse_quotaMaxNull_returnsFalse() {
        var s = newSouscription(newOrganisation(), newFormuleIllimitee(),
                StatutSouscription.ACTIVE, LocalDate.now().plusYears(1), 0, 500);
        s.setQuotaMax(null); // explicitement illimité

        assertThat(s.isQuotaDepasse()).isFalse();
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  getPlacesRestantes()
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("getPlacesRestantes: 100 max - 30 utilisés = 70 restantes")
    void getPlacesRestantes_partialUsage_returnsCorrectValue() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 30);

        assertThat(s.getPlacesRestantes()).isEqualTo(70);
    }

    @Test
    @DisplayName("getPlacesRestantes: quota plein → 0 (jamais négatif)")
    void getPlacesRestantes_quotaFull_returnsZero() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 100);

        assertThat(s.getPlacesRestantes()).isZero();
    }

    @Test
    @DisplayName("getPlacesRestantes: quotaMax null (illimité) → Integer.MAX_VALUE")
    void getPlacesRestantes_illimite_returnsMaxValue() {
        var s = newSouscription(newOrganisation(), newFormuleIllimitee(),
                StatutSouscription.ACTIVE, LocalDate.now().plusYears(1), 0, 0);
        s.setQuotaMax(null);

        assertThat(s.getPlacesRestantes()).isEqualTo(Integer.MAX_VALUE);
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  FormuleAbonnement — isIllimitee() + accepteNouveauMembre()
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("FormuleAbonnement.isIllimitee: maxMembres null → true")
    void formule_maxMembresNull_isIllimitee() {
        var f = newFormuleIllimitee();
        assertThat(f.isIllimitee()).isTrue();
    }

    @Test
    @DisplayName("FormuleAbonnement.isIllimitee: maxMembres défini → false")
    void formule_maxMembresDefined_notIllimitee() {
        var f = newFormule(200);
        assertThat(f.isIllimitee()).isFalse();
    }

    @Test
    @DisplayName("FormuleAbonnement.accepteNouveauMembre: quota non atteint → true")
    void formule_accepteNouveauMembre_quotaNotReached_returnsTrue() {
        var f = newFormule(100);
        assertThat(f.accepteNouveauMembre(99)).isTrue();
    }

    @Test
    @DisplayName("FormuleAbonnement.accepteNouveauMembre: quota atteint → false")
    void formule_accepteNouveauMembre_quotaReached_returnsFalse() {
        var f = newFormule(100);
        assertThat(f.accepteNouveauMembre(100)).isFalse();
    }

    @Test
    @DisplayName("FormuleAbonnement.accepteNouveauMembre: formule illimitée → toujours true")
    void formule_accepteNouveauMembre_illimitee_alwaysTrue() {
        var f = newFormuleIllimitee();
        assertThat(f.accepteNouveauMembre(10000)).isTrue();
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  Champs Option C sur FormuleAbonnement
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("FormuleAbonnement Option C: plan ENTERPRISE — tous les droits activés")
    void formule_enterprisePlan_allFeaturesEnabled() {
        FormuleAbonnement f = new FormuleAbonnement();
        f.setCode(TypeFormule.PREMIUM);
        f.setPlage(PlageMembres.GRANDE);
        f.setLibelle("Enterprise");
        f.setMaxMembres(null);
        f.setPrixMensuel(BigDecimal.valueOf(50000));
        f.setPrixAnnuel(BigDecimal.valueOf(500000));
        f.setPlanCommercial("ENTERPRISE");
        f.setNiveauReporting("AVANCE");
        f.setApiAccess(true);
        f.setFederationAccess(true);
        f.setSupportPrioritaire(true);
        f.setSlaGaranti("99.9%");
        f.setMaxAdmins(null); // illimité

        assertThat(f.getPlanCommercial()).isEqualTo("ENTERPRISE");
        assertThat(f.getNiveauReporting()).isEqualTo("AVANCE");
        assertThat(f.getApiAccess()).isTrue();
        assertThat(f.getFederationAccess()).isTrue();
        assertThat(f.getSupportPrioritaire()).isTrue();
        assertThat(f.getSlaGaranti()).isEqualTo("99.9%");
        assertThat(f.getMaxAdmins()).isNull();
    }

    @Test
    @DisplayName("FormuleAbonnement Option C: plan BASIC — accès limités")
    void formule_basicPlan_limitedFeatures() {
        FormuleAbonnement f = newFormule(50);
        f.setPlanCommercial("MICRO");
        f.setNiveauReporting("BASIQUE");
        f.setApiAccess(false);
        f.setFederationAccess(false);
        f.setSupportPrioritaire(false);
        f.setSlaGaranti("99.0%");
        f.setMaxAdmins(1);

        assertThat(f.getApiAccess()).isFalse();
        assertThat(f.getFederationAccess()).isFalse();
        assertThat(f.getSupportPrioritaire()).isFalse();
        assertThat(f.getMaxAdmins()).isEqualTo(1);
        assertThat(f.getNiveauReporting()).isEqualTo("BASIQUE");
    }

    // ═════════════════════════════════════════════════════════════════════════
    //  incrementerQuota() + decrementerQuota()
    // ═════════════════════════════════════════════════════════════════════════

    @Test
    @DisplayName("incrementerQuota: 30 utilisés → 31 après incrémentation")
    void incrementerQuota_increment() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 30);

        s.incrementerQuota();

        assertThat(s.getQuotaUtilise()).isEqualTo(31);
    }

    @Test
    @DisplayName("decrementerQuota: 30 utilisés → 29 après décrémentation")
    void decrementerQuota_decrement() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 30);

        s.decrementerQuota();

        assertThat(s.getQuotaUtilise()).isEqualTo(29);
    }

    @Test
    @DisplayName("decrementerQuota: 0 utilisés → reste à 0 (pas négatif)")
    void decrementerQuota_atZero_staysZero() {
        var s = newSouscription(newOrganisation(), newFormule(100),
                StatutSouscription.ACTIVE, LocalDate.now().plusMonths(1), 100, 0);

        s.decrementerQuota();

        assertThat(s.getQuotaUtilise()).isZero();
    }
}
