package dev.lions.unionflow.server.service;

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

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 dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.test.TestTransaction;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
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 pour {@link MembreService#lierMembreOrganisationEtIncrementerQuota}.
 *
 * <p>Couvre toutes les branches de la méthode :
 * <ul>
 *   <li>Arguments null → IllegalArgumentException</li>
 *   <li>Organisation introuvable → IllegalArgumentException</li>
 *   <li>Liaison réussie sans souscription active → avertissement uniquement</li>
 *   <li>Liaison réussie avec souscription ACTIVE → quota incrémenté</li>
 *   <li>Statut membre ACTIF vs autre valeur (EN_ATTENTE_VALIDATION)</li>
 * </ul>
 *
 * <p>Chaque test utilise {@code @TestTransaction} pour un rollback automatique.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-03-21
 */
@QuarkusTest
@DisplayName("MembreService — lierMembreOrganisationEtIncrementerQuota")
class MembreServiceLierMembreTest {

    @Inject
    MembreService membreService;

    @Inject
    MembreRepository membreRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    EntityManager entityManager;

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

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

    /** Crée et persiste une organisation minimale valide. */
    private Organisation creerEtPersisterOrganisation(EntityManager em) {
        Organisation org = Organisation.builder()
                .nom("Org Test Lier " + UUID.randomUUID().toString().substring(0, 8))
                .email("org.lier." + UUID.randomUUID() + "@test.com")
                .typeOrganisation("ASSOCIATION")
                .statut("ACTIF")
                .build();
        org.setDateCreation(LocalDateTime.now());
        org.setActif(true);
        em.persist(org);
        em.flush();
        return org;
    }

    /** Crée et persiste une FormuleAbonnement STARTER (supprime l'existante si besoin). */
    private FormuleAbonnement creerEtPersisterFormule(EntityManager em) {
        // Supprimer toute formule STARTER existante (provenant d'un test précédent commité)
        em.createQuery("DELETE FROM FormuleAbonnement f WHERE f.code = :code")
                .setParameter("code", TypeFormule.BASIC)
                .executeUpdate();
        em.flush();

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

    /** Crée et persiste une souscription ACTIVE pour l'organisation donnée. */
    private SouscriptionOrganisation creerEtPersisterSouscription(
            EntityManager em, Organisation organisation, FormuleAbonnement formule, int quotaUtilise) {
        SouscriptionOrganisation souscription = SouscriptionOrganisation.builder()
                .organisation(organisation)
                .formule(formule)
                .dateDebut(LocalDate.now().minusMonths(1))
                .dateFin(LocalDate.now().plusMonths(11))
                .quotaMax(50)
                .quotaUtilise(quotaUtilise)
                .statut(StatutSouscription.ACTIVE)
                .build();
        souscription.setDateCreation(LocalDateTime.now());
        souscription.setActif(true);
        em.persist(souscription);
        em.flush();
        return souscription;
    }

    // =========================================================================
    // Cas null checks
    // =========================================================================

    @Test
    @TestTransaction
    @DisplayName("Argument membre null → IllegalArgumentException")
    void lierMembre_membreNull_throwsIllegalArgument() {
        UUID orgId = UUID.randomUUID();
        assertThatThrownBy(() -> membreService.lierMembreOrganisationEtIncrementerQuota(null, orgId, "ACTIF"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Membre et organisationId obligatoires");
    }

    @Test
    @TestTransaction
    @DisplayName("Argument organisationId null → IllegalArgumentException")
    void lierMembre_organisationIdNull_throwsIllegalArgument() {
        Membre membre = creerEtPersisterMembre(entityManager);
        assertThatThrownBy(() ->
                membreService.lierMembreOrganisationEtIncrementerQuota(membre, null, "ACTIF"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Membre et organisationId obligatoires");
    }

    // =========================================================================
    // Organisation introuvable
    // =========================================================================

    @Test
    @TestTransaction
    @DisplayName("Organisation inexistante → IllegalArgumentException contenant l'UUID")
    void lierMembre_organisationInexistante_throwsIllegalArgument() {
        Membre membre = creerEtPersisterMembre(entityManager);
        UUID orgIdInexistant = UUID.randomUUID();

        assertThatThrownBy(() ->
                membreService.lierMembreOrganisationEtIncrementerQuota(membre, orgIdInexistant, "ACTIF"))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("Organisation non trouvée")
                .hasMessageContaining(orgIdInexistant.toString());
    }

    // =========================================================================
    // Liaison réussie sans souscription active
    // =========================================================================

    @Test
    @TestTransaction
    @DisplayName("Liaison réussie sans souscription → MembreOrganisation persisté, statut ACTIF")
    void lierMembre_sansSouscription_creeMembreOrganisationAvecStatutActif() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);

        // Aucune souscription persistée pour cet org → branche "aucune souscription active"
        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "ACTIF");
        entityManager.flush();

        // Vérifier que le lien MembreOrganisation a été créé
        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);
        assertThat(liens.get(0).getDateAdhesion()).isEqualTo(LocalDate.now());
    }

    @Test
    @TestTransaction
    @DisplayName("Liaison réussie sans souscription → statut EN_ATTENTE_VALIDATION quand type != ACTIF")
    void lierMembre_sansSouscription_typeEnAttente_creeMembreOrganisationAvecStatutEnAttente() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);

        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "EN_ATTENTE");
        entityManager.flush();

        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);
    }

    // =========================================================================
    // Liaison réussie avec souscription ACTIVE → quota incrémenté
    // =========================================================================

    @Test
    @TestTransaction
    @DisplayName("Liaison avec souscription ACTIVE → quota incrémenté de 1")
    void lierMembre_avecSouscriptionActive_incrementeQuota() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);
        FormuleAbonnement formule = creerEtPersisterFormule(entityManager);
        SouscriptionOrganisation souscription = creerEtPersisterSouscription(entityManager, org, formule, 5);

        int quotaAvant = souscription.getQuotaUtilise();

        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "ACTIF");
        entityManager.flush();
        entityManager.refresh(souscription);

        assertThat(souscription.getQuotaUtilise()).isEqualTo(quotaAvant + 1);
    }

    @Test
    @TestTransaction
    @DisplayName("Liaison avec souscription ACTIVE → MembreOrganisation créé ET quota incrémenté")
    void lierMembre_avecSouscriptionActive_creeMembreOrganisationEtIncrementeQuota() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);
        FormuleAbonnement formule = creerEtPersisterFormule(entityManager);
        creerEtPersisterSouscription(entityManager, org, formule, 0);

        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "ACTIF");
        entityManager.flush();

        // MembreOrganisation créé
        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);

        // Quota incrémenté
        SouscriptionOrganisation souscriptionVerif = entityManager
                .createQuery(
                        "SELECT s FROM SouscriptionOrganisation s WHERE s.organisation.id = :orgId AND s.statut = 'ACTIVE'",
                        SouscriptionOrganisation.class)
                .setParameter("orgId", org.getId())
                .getSingleResult();
        assertThat(souscriptionVerif.getQuotaUtilise()).isEqualTo(1);
    }

    @Test
    @TestTransaction
    @DisplayName("Liaison avec souscription ACTIVE → quota incrémenté depuis 0 → quotaUtilise = 1")
    void lierMembre_avecSouscriptionActive_quotaPartantDeZero_devientUn() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);
        FormuleAbonnement formule = creerEtPersisterFormule(entityManager);
        SouscriptionOrganisation souscription = creerEtPersisterSouscription(entityManager, org, formule, 0);

        assertThat(souscription.getQuotaUtilise()).isEqualTo(0);

        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "actif"); // casse mixte
        entityManager.flush();
        entityManager.refresh(souscription);

        assertThat(souscription.getQuotaUtilise()).isEqualTo(1);
    }

    // =========================================================================
    // Branche typeMembreDefaut insensible à la casse (ACTIF vs actif)
    // =========================================================================

    @Test
    @TestTransaction
    @DisplayName("typeMembreDefaut en minuscules ne correspond pas à 'ACTIF' → statut EN_ATTENTE_VALIDATION")
    void lierMembre_typeMembreMinuscule_donneEnAttente() {
        // La comparaison dans le code source est "ACTIF".equalsIgnoreCase(typeMembreDefaut)
        // donc "actif" → ACTIF. Vérifions le comportement exact.
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);

        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), "actif");
        entityManager.flush();

        List<MembreOrganisation> liens = entityManager
                .createQuery(
                        "SELECT mo FROM MembreOrganisation mo WHERE mo.membre.id = :membreId",
                        MembreOrganisation.class)
                .setParameter("membreId", membre.getId())
                .getResultList();

        assertThat(liens).hasSize(1);
        // equalsIgnoreCase → "actif" est traité comme ACTIF
        assertThat(liens.get(0).getStatutMembre())
                .isEqualTo(dev.lions.unionflow.server.api.enums.membre.StatutMembre.ACTIF);
    }

    @Test
    @TestTransaction
    @DisplayName("typeMembreDefaut null → statut EN_ATTENTE_VALIDATION")
    void lierMembre_typeMembreNull_donneEnAttente() {
        Membre membre = creerEtPersisterMembre(entityManager);
        Organisation org = creerEtPersisterOrganisation(entityManager);

        // "ACTIF".equalsIgnoreCase(null) → false → EN_ATTENTE_VALIDATION
        membreService.lierMembreOrganisationEtIncrementerQuota(membre, org.getId(), null);
        entityManager.flush();

        List<MembreOrganisation> liens = entityManager
                .createQuery(
                        "SELECT mo FROM MembreOrganisation mo WHERE mo.membre.id = :membreId",
                        MembreOrganisation.class)
                .setParameter("membreId", membre.getId())
                .getResultList();

        assertThat(liens).hasSize(1);
        assertThat(liens.get(0).getStatutMembre())
                .isEqualTo(dev.lions.unionflow.server.api.enums.membre.StatutMembre.EN_ATTENTE_VALIDATION);
    }
}
