package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.api.dto.dashboard.DashboardDataDTO;
import dev.lions.unionflow.server.api.dto.dashboard.DashboardStatsDTO;
import dev.lions.unionflow.server.api.dto.dashboard.RecentActivityDTO;
import dev.lions.unionflow.server.api.dto.dashboard.UpcomingEventDTO;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.api.service.dashboard.DashboardService;
import dev.lions.unionflow.server.entity.Cotisation;
import dev.lions.unionflow.server.entity.DemandeAide;
import dev.lions.unionflow.server.entity.Evenement;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
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.persistence.TypedQuery;
import org.jboss.logging.Logger;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Implémentation du service Dashboard pour Quarkus
 *
 * <p>Cette implémentation récupère les données réelles depuis la base de données
 * via les repositories.
 *
 * @author UnionFlow Team
 * @version 2.0
 * @since 2025-01-17
 */
@ApplicationScoped
public class DashboardServiceImpl implements DashboardService {

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

    @Inject
    MembreRepository membreRepository;

    @Inject
    EvenementRepository evenementRepository;

    @Inject
    CotisationRepository cotisationRepository;

    @Inject
    DemandeAideRepository demandeAideRepository;

    @Inject
    OrganisationRepository organisationRepository;

    @Override
    public DashboardDataDTO getDashboardData(String organizationId, String userId) {
        LOG.infof("Récupération des données dashboard pour org: %s et user: %s", organizationId, userId);

        UUID orgId = UUID.fromString(organizationId);

        return DashboardDataDTO.builder()
                .stats(getDashboardStats(organizationId, userId))
                .recentActivities(getRecentActivities(organizationId, userId, 10))
                .upcomingEvents(getUpcomingEvents(organizationId, userId, 5))
                .userPreferences(getUserPreferences(userId))
                .organizationId(organizationId)
                .userId(userId)
                .build();
    }

    @Override
    public DashboardStatsDTO getDashboardStats(String organizationId, String userId) {
        LOG.infof("Récupération des stats dashboard pour org: %s et user: %s", organizationId, userId);

        UUID orgId = UUID.fromString(organizationId);

        // Compter les membres
        long totalMembers = membreRepository.count();
        long activeMembers = membreRepository.countActifs();

        // Compter les événements
        long totalEvents = evenementRepository.count();
        long upcomingEvents = evenementRepository.findEvenementsAVenir().size();

        // Compter les cotisations
        long totalContributions = cotisationRepository.count();
        BigDecimal totalContributionAmount = calculateTotalContributionAmount(orgId);

        // Compter les demandes en attente
        List<DemandeAide> pendingRequests = demandeAideRepository.findByStatut(StatutAide.EN_ATTENTE);
        long pendingRequestsCount = pendingRequests.stream()
            .filter(d -> d.getOrganisation() != null && d.getOrganisation().getId().equals(orgId))
            .count();

        // Calculer la croissance mensuelle (membres ajoutés ce mois)
        LocalDate debutMois = LocalDate.now().withDayOfMonth(1);
        long nouveauxMembresMois = membreRepository.countNouveauxMembres(debutMois);
        long totalMembresAvant = totalMembers - nouveauxMembresMois;
        double monthlyGrowth = totalMembresAvant > 0 
            ? (double) nouveauxMembresMois / totalMembresAvant * 100.0 
            : 0.0;

        // Calculer le taux d'engagement (membres actifs / total)
        double engagementRate = totalMembers > 0 
            ? (double) activeMembers / totalMembers 
            : 0.0;

        return DashboardStatsDTO.builder()
                .totalMembers((int) totalMembers)
                .activeMembers((int) activeMembers)
                .totalEvents((int) totalEvents)
                .upcomingEvents((int) upcomingEvents)
                .totalContributions((int) totalContributions)
                .totalContributionAmount(totalContributionAmount.doubleValue())
                .pendingRequests((int) pendingRequestsCount)
                .completedProjects(0) // À implémenter si nécessaire
                .monthlyGrowth(monthlyGrowth)
                .engagementRate(engagementRate)
                .lastUpdated(LocalDateTime.now())
                .build();
    }

    @Override
    public List<RecentActivityDTO> getRecentActivities(String organizationId, String userId, int limit) {
        LOG.infof("Récupération de %d activités récentes pour org: %s et user: %s", limit, organizationId, userId);

        UUID orgId = UUID.fromString(organizationId);
        List<RecentActivityDTO> activities = new ArrayList<>();

        // Récupérer les membres récemment créés
        List<Membre> nouveauxMembres = membreRepository.rechercheAvancee(
            null, true, null, null, Page.of(0, limit), Sort.by("dateCreation", Sort.Direction.Descending));
        
        for (Membre membre : nouveauxMembres) {
            if (membre.getOrganisation() != null && membre.getOrganisation().getId().equals(orgId)) {
                activities.add(RecentActivityDTO.builder()
                    .id(membre.getId().toString())
                    .type("member")
                    .title("Nouveau membre inscrit")
                    .description(membre.getNomComplet() + " a rejoint l'organisation")
                    .userName(membre.getNomComplet())
                    .timestamp(membre.getDateCreation())
                    .userAvatar(null)
                    .actionUrl("/members/" + membre.getId())
                    .build());
            }
        }

        // Récupérer les événements récemment créés
        List<Evenement> tousEvenements = evenementRepository.listAll();
        List<Evenement> nouveauxEvenements = tousEvenements.stream()
            .filter(e -> e.getOrganisation() != null && e.getOrganisation().getId().equals(orgId))
            .sorted(Comparator.comparing(Evenement::getDateCreation).reversed())
            .limit(limit)
            .collect(Collectors.toList());
        
        for (Evenement evenement : nouveauxEvenements) {
            activities.add(RecentActivityDTO.builder()
                .id(evenement.getId().toString())
                .type("event")
                .title("Événement créé")
                .description(evenement.getTitre() + " a été programmé")
                .userName(evenement.getOrganisation() != null ? evenement.getOrganisation().getNom() : "Système")
                .timestamp(evenement.getDateCreation())
                .userAvatar(null)
                .actionUrl("/events/" + evenement.getId())
                .build());
        }

        // Récupérer les cotisations récentes
        List<Cotisation> cotisationsRecentes = cotisationRepository.rechercheAvancee(
            null, "PAYEE", null, null, null, Page.of(0, limit));
        
        for (Cotisation cotisation : cotisationsRecentes) {
            if (cotisation.getMembre() != null && 
                cotisation.getMembre().getOrganisation() != null && 
                cotisation.getMembre().getOrganisation().getId().equals(orgId)) {
                activities.add(RecentActivityDTO.builder()
                    .id(cotisation.getId().toString())
                    .type("contribution")
                    .title("Cotisation reçue")
                    .description("Paiement de " + cotisation.getMontantPaye() + " " + cotisation.getCodeDevise() + " reçu")
                    .userName(cotisation.getMembre().getNomComplet())
                    .timestamp(cotisation.getDatePaiement() != null ? cotisation.getDatePaiement() : cotisation.getDateCreation())
                    .userAvatar(null)
                    .actionUrl("/contributions/" + cotisation.getId())
                    .build());
            }
        }

        // Trier par timestamp décroissant et limiter
        return activities.stream()
            .sorted(Comparator.comparing(RecentActivityDTO::getTimestamp).reversed())
            .limit(limit)
            .collect(Collectors.toList());
    }

    @Override
    public List<UpcomingEventDTO> getUpcomingEvents(String organizationId, String userId, int limit) {
        LOG.infof("Récupération de %d événements à venir pour org: %s et user: %s", limit, organizationId, userId);

        UUID orgId = UUID.fromString(organizationId);
        
        List<Evenement> evenements = evenementRepository.findEvenementsAVenir(
            Page.of(0, limit), Sort.by("dateDebut", Sort.Direction.Ascending));

        return evenements.stream()
            .filter(e -> e.getOrganisation() == null || e.getOrganisation().getId().equals(orgId))
            .map(this::convertToUpcomingEventDTO)
            .limit(limit)
            .collect(Collectors.toList());
    }

    private UpcomingEventDTO convertToUpcomingEventDTO(Evenement evenement) {
        return UpcomingEventDTO.builder()
            .id(evenement.getId().toString())
            .title(evenement.getTitre())
            .description(evenement.getDescription())
            .startDate(evenement.getDateDebut())
            .endDate(evenement.getDateFin())
            .location(evenement.getLieu())
            .maxParticipants(evenement.getCapaciteMax())
            .currentParticipants(evenement.getNombreInscrits())
            .status(evenement.getStatut() != null ? evenement.getStatut().name() : "PLANIFIE")
            .imageUrl(null)
            .tags(Collections.emptyList())
            .build();
    }

    private BigDecimal calculateTotalContributionAmount(UUID organisationId) {
        TypedQuery<BigDecimal> query = cotisationRepository.getEntityManager().createQuery(
            "SELECT COALESCE(SUM(c.montantDu), 0) FROM Cotisation c WHERE c.membre.organisation.id = :organisationId",
            BigDecimal.class);
        query.setParameter("organisationId", organisationId);
        BigDecimal result = query.getSingleResult();
        return result != null ? result : BigDecimal.ZERO;
    }

    private Map<String, Object> getUserPreferences(String userId) {
        Map<String, Object> preferences = new HashMap<>();
        preferences.put("theme", "royal_teal");
        preferences.put("language", "fr");
        preferences.put("notifications", true);
        preferences.put("autoRefresh", true);
        preferences.put("refreshInterval", 300);
        return preferences;
    }
}
