package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.Evenement.StatutEvenement;
import dev.lions.unionflow.server.entity.Evenement.TypeEvenement;
import dev.lions.unionflow.server.repository.EvenementRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.jboss.logging.Logger;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Service métier pour la gestion des événements
 * Version simplifiée pour tester les imports et Lombok
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@ApplicationScoped
public class EvenementService {

    private static final Logger LOG = Logger.getLogger(EvenementService.class);

    @Inject
    EvenementRepository evenementRepository;

    @Inject
    MembreRepository membreRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Inject
    KeycloakService keycloakService;

    /**
     * Crée un nouvel événement
     *
     * @param evenement l'événement à créer
     * @return l'événement créé
     * @throws IllegalArgumentException si les données sont invalides
     */
    @Transactional
    public Evenement creerEvenement(Evenement evenement) {
        LOG.infof("Création événement: %s", evenement.getTitre());

        // Validation des données
        validerEvenement(evenement);

        // Vérifier l'unicité du titre dans l'organisation
        if (evenement.getOrganisation() != null) {
            Optional<Evenement> existant = evenementRepository.findByTitre(evenement.getTitre());
            if (existant.isPresent() &&
                existant.get().getOrganisation().id.equals(evenement.getOrganisation().id)) {
                throw new IllegalArgumentException("Un événement avec ce titre existe déjà dans cette organisation");
            }
        }

        // Métadonnées de création
        evenement.setCreePar(keycloakService.getCurrentUserEmail());
        evenement.setDateCreation(LocalDateTime.now());

        // Valeurs par défaut
        if (evenement.getStatut() == null) {
            evenement.setStatut(StatutEvenement.PLANIFIE);
        }
        if (evenement.getActif() == null) {
            evenement.setActif(true);
        }
        if (evenement.getVisiblePublic() == null) {
            evenement.setVisiblePublic(true);
        }
        if (evenement.getInscriptionRequise() == null) {
            evenement.setInscriptionRequise(true);
        }

        evenement.persist();

        LOG.infof("Événement créé avec succès: ID=%d, Titre=%s", evenement.id, evenement.getTitre());
        return evenement;
    }

    /**
     * Met à jour un événement existant
     *
     * @param id l'ID de l'événement
     * @param evenementMisAJour les nouvelles données
     * @return l'événement mis à jour
     * @throws IllegalArgumentException si l'événement n'existe pas
     */
    @Transactional
    public Evenement mettreAJourEvenement(Long id, Evenement evenementMisAJour) {
        LOG.infof("Mise à jour événement ID: %d", id);

        Evenement evenementExistant = evenementRepository.findByIdOptional(id)
            .orElseThrow(() -> new IllegalArgumentException("Événement non trouvé avec l'ID: " + id));

        // Vérifier les permissions
        if (!peutModifierEvenement(evenementExistant)) {
            throw new SecurityException("Vous n'avez pas les permissions pour modifier cet événement");
        }

        // Validation des nouvelles données
        validerEvenement(evenementMisAJour);

        // Mise à jour des champs
        evenementExistant.setTitre(evenementMisAJour.getTitre());
        evenementExistant.setDescription(evenementMisAJour.getDescription());
        evenementExistant.setDateDebut(evenementMisAJour.getDateDebut());
        evenementExistant.setDateFin(evenementMisAJour.getDateFin());
        evenementExistant.setLieu(evenementMisAJour.getLieu());
        evenementExistant.setAdresse(evenementMisAJour.getAdresse());
        evenementExistant.setTypeEvenement(evenementMisAJour.getTypeEvenement());
        evenementExistant.setCapaciteMax(evenementMisAJour.getCapaciteMax());
        evenementExistant.setPrix(evenementMisAJour.getPrix());
        evenementExistant.setInscriptionRequise(evenementMisAJour.getInscriptionRequise());
        evenementExistant.setDateLimiteInscription(evenementMisAJour.getDateLimiteInscription());
        evenementExistant.setInstructionsParticulieres(evenementMisAJour.getInstructionsParticulieres());
        evenementExistant.setContactOrganisateur(evenementMisAJour.getContactOrganisateur());
        evenementExistant.setMaterielRequis(evenementMisAJour.getMaterielRequis());
        evenementExistant.setVisiblePublic(evenementMisAJour.getVisiblePublic());

        // Métadonnées de modification
        evenementExistant.setModifiePar(keycloakService.getCurrentUserEmail());
        evenementExistant.setDateModification(LocalDateTime.now());

        evenementExistant.persist();

        LOG.infof("Événement mis à jour avec succès: ID=%d", id);
        return evenementExistant;
    }

    /**
     * Trouve un événement par ID
     */
    public Optional<Evenement> trouverParId(Long id) {
        return evenementRepository.findByIdOptional(id);
    }

    /**
     * Liste tous les événements actifs avec pagination
     */
    public List<Evenement> listerEvenementsActifs(Page page, Sort sort) {
        return evenementRepository.findAllActifs(page, sort);
    }

    /**
     * Liste les événements à venir
     */
    public List<Evenement> listerEvenementsAVenir(Page page, Sort sort) {
        return evenementRepository.findEvenementsAVenir(page, sort);
    }

    /**
     * Liste les événements publics
     */
    public List<Evenement> listerEvenementsPublics(Page page, Sort sort) {
        return evenementRepository.findEvenementsPublics(page, sort);
    }

    /**
     * Recherche d'événements par terme
     */
    public List<Evenement> rechercherEvenements(String terme, Page page, Sort sort) {
        return evenementRepository.rechercheAvancee(terme, null, null, null, null,
                                                   null, null, null, null, null, page, sort);
    }

    /**
     * Liste les événements par type
     */
    public List<Evenement> listerParType(TypeEvenement type, Page page, Sort sort) {
        return evenementRepository.findByType(type, page, sort);
    }

    /**
     * Supprime logiquement un événement
     *
     * @param id l'ID de l'événement à supprimer
     * @throws IllegalArgumentException si l'événement n'existe pas
     */
    @Transactional
    public void supprimerEvenement(Long id) {
        LOG.infof("Suppression événement ID: %d", id);

        Evenement evenement = evenementRepository.findByIdOptional(id)
            .orElseThrow(() -> new IllegalArgumentException("Événement non trouvé avec l'ID: " + id));

        // Vérifier les permissions
        if (!peutModifierEvenement(evenement)) {
            throw new SecurityException("Vous n'avez pas les permissions pour supprimer cet événement");
        }

        // Vérifier s'il y a des inscriptions
        if (evenement.getNombreInscrits() > 0) {
            throw new IllegalStateException("Impossible de supprimer un événement avec des inscriptions");
        }

        // Suppression logique
        evenement.setActif(false);
        evenement.setModifiePar(keycloakService.getCurrentUserEmail());
        evenement.setDateModification(LocalDateTime.now());

        evenement.persist();

        LOG.infof("Événement supprimé avec succès: ID=%d", id);
    }

    /**
     * Change le statut d'un événement
     *
     * @param id l'ID de l'événement
     * @param nouveauStatut le nouveau statut
     * @return l'événement mis à jour
     */
    @Transactional
    public Evenement changerStatut(Long id, StatutEvenement nouveauStatut) {
        LOG.infof("Changement statut événement ID: %d vers %s", id, nouveauStatut);

        Evenement evenement = evenementRepository.findByIdOptional(id)
            .orElseThrow(() -> new IllegalArgumentException("Événement non trouvé avec l'ID: " + id));

        // Vérifier les permissions
        if (!peutModifierEvenement(evenement)) {
            throw new SecurityException("Vous n'avez pas les permissions pour modifier cet événement");
        }

        // Valider le changement de statut
        validerChangementStatut(evenement.getStatut(), nouveauStatut);

        evenement.setStatut(nouveauStatut);
        evenement.setModifiePar(keycloakService.getCurrentUserEmail());
        evenement.setDateModification(LocalDateTime.now());

        evenement.persist();

        LOG.infof("Statut événement changé avec succès: ID=%d, Nouveau statut=%s", id, nouveauStatut);
        return evenement;
    }

    /**
     * Obtient les statistiques des événements
     *
     * @return les statistiques sous forme de Map
     */
    public Map<String, Object> obtenirStatistiques() {
        Map<String, Long> statsBase = evenementRepository.getStatistiques();

        long total = statsBase.getOrDefault("total", 0L);
        long actifs = statsBase.getOrDefault("actifs", 0L);
        long aVenir = statsBase.getOrDefault("aVenir", 0L);
        long enCours = statsBase.getOrDefault("enCours", 0L);

        Map<String, Object> result = new java.util.HashMap<>();
        result.put("total", total);
        result.put("actifs", actifs);
        result.put("aVenir", aVenir);
        result.put("enCours", enCours);
        result.put("passes", statsBase.getOrDefault("passes", 0L));
        result.put("publics", statsBase.getOrDefault("publics", 0L));
        result.put("avecInscription", statsBase.getOrDefault("avecInscription", 0L));
        result.put("tauxActivite", total > 0 ? (actifs * 100.0 / total) : 0.0);
        result.put("tauxEvenementsAVenir", total > 0 ? (aVenir * 100.0 / total) : 0.0);
        result.put("tauxEvenementsEnCours", total > 0 ? (enCours * 100.0 / total) : 0.0);
        result.put("timestamp", LocalDateTime.now());
        return result;
    }

    // Méthodes privées de validation et permissions

    /**
     * Valide les données d'un événement
     */
    private void validerEvenement(Evenement evenement) {
        if (evenement.getTitre() == null || evenement.getTitre().trim().isEmpty()) {
            throw new IllegalArgumentException("Le titre de l'événement est obligatoire");
        }

        if (evenement.getDateDebut() == null) {
            throw new IllegalArgumentException("La date de début est obligatoire");
        }

        if (evenement.getDateDebut().isBefore(LocalDateTime.now().minusHours(1))) {
            throw new IllegalArgumentException("La date de début ne peut pas être dans le passé");
        }

        if (evenement.getDateFin() != null && evenement.getDateFin().isBefore(evenement.getDateDebut())) {
            throw new IllegalArgumentException("La date de fin ne peut pas être antérieure à la date de début");
        }

        if (evenement.getCapaciteMax() != null && evenement.getCapaciteMax() <= 0) {
            throw new IllegalArgumentException("La capacité maximale doit être positive");
        }

        if (evenement.getPrix() != null && evenement.getPrix().compareTo(java.math.BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("Le prix ne peut pas être négatif");
        }
    }

    /**
     * Valide un changement de statut
     */
    private void validerChangementStatut(StatutEvenement statutActuel, StatutEvenement nouveauStatut) {
        // Règles de transition simplifiées pour la version mobile
        if (statutActuel == StatutEvenement.TERMINE || statutActuel == StatutEvenement.ANNULE) {
            throw new IllegalArgumentException("Impossible de changer le statut d'un événement terminé ou annulé");
        }
    }

    /**
     * Vérifie les permissions de modification pour l'application mobile
     */
    private boolean peutModifierEvenement(Evenement evenement) {
        if (keycloakService.hasRole("ADMIN") || keycloakService.hasRole("ORGANISATEUR_EVENEMENT")) {
            return true;
        }

        String utilisateurActuel = keycloakService.getCurrentUserEmail();
        return utilisateurActuel != null && utilisateurActuel.equals(evenement.getCreePar());
    }
}
