package dev.lions.unionflow.server.service;

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

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.entity.FormuleAbonnement;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.MembreOrganisation;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.SouscriptionOrganisation;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;

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

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

/**
 * Tests ciblant la branche non couverte L1018 dans
 * {@link MembreService#lierMembreOrganisationEtIncrementerQuota}.
 *
 * <p>Branche couverte :
 * <ul>
 *   <li><b>L1018</b> : ternaire
 *       {@code souscription.getQuotaMax() != null ? souscription.getQuotaMax().toString() : "∞"}
 *       dans le {@code LOG.infof(...)} après l'incrémentation du quota.
 *       <br>Branche false : {@code quotaMax == null} → utilise la chaîne {@code "∞"} dans le log.
 *       <br>Déclenchée en persistant une {@link SouscriptionOrganisation} avec
 *       {@code quotaMax = null} (souscription illimitée).
 *   </li>
 * </ul>
 *
 * <p>Le fichier de test existant {@code MembreServiceLierMembreTest.java} crée toujours des
 * souscriptions avec {@code quotaMax = 50} (non null) → la branche false de L1018 n'est
 * jamais atteinte.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-23
 */
@QuarkusTest
@DisplayName("MembreService — lierMembreOrganisationEtIncrementerQuota: quotaMax null (L1018)")
class MembreServiceLierMembreQuotaMaxNullTest {

    @Inject
    MembreService membreService;

    @Inject
    EntityManager entityManager;

    // =========================================================================
    // Helpers
    // =========================================================================

    /** Crée et persiste un membre minimal valide. */
    private Membre creerMembre() {
        Membre membre = Membre.builder()
                .numeroMembre("UF-L1018-" + UUID.randomUUID().toString().substring(0, 8).toUpperCase())
                .prenom("QuotaNull")
                .nom("Test")
                .email("quotanull." + UUID.randomUUID() + "@test.com")
                .dateNaissance(LocalDate.of(1990, 1, 1))
                .build();
        membre.setDateCreation(LocalDateTime.now());
        membre.setActif(true);
        membre.setStatutCompte("ACTIF");
        entityManager.persist(membre);
        entityManager.flush();
        return membre;
    }

    /** Crée et persiste une organisation minimale valide. */
    private Organisation creerOrganisation() {
        Organisation org = Organisation.builder()
                .nom("Org L1018 QMax Null " + UUID.randomUUID().toString().substring(0, 8))
                .email("org.l1018.null." + UUID.randomUUID() + "@test.com")
                .typeOrganisation("ASSOCIATION")
                .statut("ACTIF")
                .build();
        org.setDateCreation(LocalDateTime.now());
        org.setActif(true);
        entityManager.persist(org);
        entityManager.flush();
        return org;
    }

    /** Crée et persiste une FormuleAbonnement STARTER (supprime l'existante si besoin). */
    private FormuleAbonnement creerFormule() {
        entityManager.createQuery("DELETE FROM FormuleAbonnement f WHERE f.code = :code")
                .setParameter("code", TypeFormule.BASIC)
                .executeUpdate();
        entityManager.flush();

        FormuleAbonnement formule = FormuleAbonnement.builder()
                .code(TypeFormule.BASIC)
                .plage(PlageMembres.PETITE)
                .libelle("Basic L1018 QuotaNull")
                .maxMembres(999)
                .prixMensuel(BigDecimal.valueOf(5000))
                .prixAnnuel(BigDecimal.valueOf(55000))
                .build();
        formule.setDateCreation(LocalDateTime.now());
        formule.setActif(true);
        entityManager.persist(formule);
        entityManager.flush();
        return formule;
    }

    // =========================================================================
    // L1018 : ternaire quotaMax != null dans lierMembreOrganisationEtIncrementerQuota
    //
    // Code source (L1012-1021) :
    //   if (souscriptionOpt.isPresent()) {
    //     SouscriptionOrganisation souscription = souscriptionOpt.get();
    //     souscription.incrementerQuota();
    //     entityManager.persist(souscription);
    //     LOG.infof("Quota souscription incrémenté (utilise: %d/%s)",  ← L1016-L1017
    //               souscription.getQuotaUtilise(),
    //               souscription.getQuotaMax() != null                  ← L1018 (ternaire)
    //                   ? souscription.getQuotaMax().toString()         ← L1018 (branche true)
    //                   : "∞");                                         ← L1018 (branche false)
    //   }
    //
    // Les tests existants utilisent quotaMax = 50 → branche true (toString()) toujours atteinte.
    // Pour couvrir la branche false ("∞") : quotaMax = null.
    // =========================================================================

    /**
     * Couvre la ligne 1018 de MembreService, branche false :
     * {@code souscription.getQuotaMax() != null ? ... : "∞"}
     *
     * <p>On crée une souscription ACTIVE avec {@code quotaMax = null} (quota illimité).
     * Lors de l'appel à {@code lierMembreOrganisationEtIncrementerQuota}, la souscription est
     * trouvée, le quota est incrémenté, et le log utilise {@code "∞"} (branche false).
     *
     * <p>Résultats vérifiés :
     * <ul>
     *   <li>Le {@link MembreOrganisation} est bien créé.</li>
     *   <li>Le {@code quotaUtilise} est incrémenté.</li>
     *   <li>Le {@code quotaMax} reste null (non modifié par la méthode).</li>
     * </ul>
     */
    @Test
    @TestTransaction
    @DisplayName("souscription.getQuotaMax() == null → ternaire L1018 branche false ('∞') atteinte")
    void lierMembre_souscriptionAvecQuotaMaxNull_ternaireFalse_logueTiret() {
        Membre membre = creerMembre();
        Organisation org = creerOrganisation();
        FormuleAbonnement formule = creerFormule();

        // Souscription ACTIVE avec quotaMax = null (illimitée)
        // → lorsque le LOG.infof est exécuté en L1018, getQuotaMax() retourne null
        // → la branche false du ternaire est atteinte → "∞" est utilisé
        SouscriptionOrganisation souscription = SouscriptionOrganisation.builder()
                .organisation(org)
                .formule(formule)
                .dateDebut(LocalDate.now().minusMonths(1))
                .dateFin(LocalDate.now().plusMonths(11))
                .quotaMax(null)            // ← null → branche false L1018 → "∞"
                .quotaUtilise(0)
                .statut(StatutSouscription.ACTIVE)
                .build();
        souscription.setDateCreation(LocalDateTime.now());
        souscription.setActif(true);
        entityManager.persist(souscription);
        entityManager.flush();

        // Le @PrePersist de SouscriptionOrganisation fixe automatiquement
        // quotaMax = formule.getMaxMembres() (999) quand quotaMax == null && formule != null.
        // On force quotaMax à null via JPQL UPDATE pour simuler une souscription illimitée
        // et couvrir la branche false L1018 ("∞") lors du LOG.infof.
        entityManager.createQuery(
                "UPDATE SouscriptionOrganisation s SET s.quotaMax = null WHERE s.id = :id")
                .setParameter("id", souscription.getId())
                .executeUpdate();
        entityManager.flush();
        entityManager.refresh(souscription);

        int quotaAvant = souscription.getQuotaUtilise(); // 0

        // Appel → atteint la branche souscriptionOpt.isPresent() = true
        // → souscription.incrementerQuota()
        // → LOG.infof avec souscription.getQuotaMax() == null → branche false → "∞"
        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "ACTIF");
        entityManager.flush();
        entityManager.refresh(souscription);

        // Vérifier que le MembreOrganisation a été créé (liaison effectuée)
        List<MembreOrganisation> liens = entityManager
                .createQuery(
                        "SELECT mo FROM MembreOrganisation mo "
                        + "WHERE mo.membre.id = :membreId AND mo.organisation.id = :orgId",
                        MembreOrganisation.class)
                .setParameter("membreId", membre.getId())
                .setParameter("orgId", org.getId())
                .getResultList();
        assertThat(liens).hasSize(1);
        assertThat(liens.get(0).getStatutMembre())
                .isEqualTo(dev.lions.unionflow.server.api.enums.membre.StatutMembre.ACTIF);

        // Quota incrémenté de 0 → 1
        assertThat(souscription.getQuotaUtilise()).isEqualTo(quotaAvant + 1);

        // quotaMax reste null (non modifié par lierMembreOrganisationEtIncrementerQuota)
        assertThat(souscription.getQuotaMax()).isNull();
    }

    /**
     * Variante : quotaMax = null avec statut EN_ATTENTE_VALIDATION pour le membre.
     * Couvre à nouveau L1018 (branche false) avec un type de membre différent.
     */
    @Test
    @TestTransaction
    @DisplayName("souscription quotaMax=null + type EN_ATTENTE → L1018 branche false, statut EN_ATTENTE_VALIDATION")
    void lierMembre_souscriptionQuotaMaxNull_typeEnAttente_membreEnAttenteEtL1018False() {
        Membre membre = creerMembre();
        Organisation org = creerOrganisation();
        FormuleAbonnement formule = creerFormule();

        SouscriptionOrganisation souscription = SouscriptionOrganisation.builder()
                .organisation(org)
                .formule(formule)
                .dateDebut(LocalDate.now().minusMonths(2))
                .dateFin(LocalDate.now().plusMonths(10))
                .quotaMax(null)            // ← null → branche false L1018
                .quotaUtilise(5)
                .statut(StatutSouscription.ACTIVE)
                .build();
        souscription.setDateCreation(LocalDateTime.now());
        souscription.setActif(true);
        entityManager.persist(souscription);
        entityManager.flush();

        // Même raison que le premier test : @PrePersist fixe quotaMax = formule.getMaxMembres().
        // On force null via JPQL UPDATE.
        entityManager.createQuery(
                "UPDATE SouscriptionOrganisation s SET s.quotaMax = null WHERE s.id = :id")
                .setParameter("id", souscription.getId())
                .executeUpdate();
        entityManager.flush();
        entityManager.refresh(souscription);

        // Type EN_ATTENTE → "ACTIF".equalsIgnoreCase("EN_ATTENTE") = false → EN_ATTENTE_VALIDATION
        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "EN_ATTENTE");
        entityManager.flush();
        entityManager.refresh(souscription);

        // MembreOrganisation créé avec statut EN_ATTENTE_VALIDATION
        List<MembreOrganisation> liens = entityManager
                .createQuery(
                        "SELECT mo FROM MembreOrganisation mo "
                        + "WHERE mo.membre.id = :membreId AND mo.organisation.id = :orgId",
                        MembreOrganisation.class)
                .setParameter("membreId", membre.getId())
                .setParameter("orgId", org.getId())
                .getResultList();
        assertThat(liens).hasSize(1);
        assertThat(liens.get(0).getStatutMembre())
                .isEqualTo(dev.lions.unionflow.server.api.enums.membre.StatutMembre.EN_ATTENTE_VALIDATION);

        // Quota incrémenté de 5 → 6 (et L1018 branche false "∞" atteinte dans le log)
        assertThat(souscription.getQuotaUtilise()).isEqualTo(6);
        assertThat(souscription.getQuotaMax()).isNull();
    }
}
