package com.social.media.domain.analytics.service;

import com.social.media.domain.analytics.aggregate.AccountMetrics;
import com.social.media.domain.analytics.aggregate.PostMetrics;
import com.social.media.domain.analytics.valueobject.MetricType;
import com.social.media.domain.analytics.valueobject.MetricValue;
import com.social.media.domain.analytics.valueobject.TimePeriod;

import java.util.List;
import java.util.Map;
import java.util.EnumMap;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * Domain service for analytics calculations and aggregations
 */
public class AnalyticsCalculationService {
    
    public Map<MetricType, MetricValue> aggregateMetrics(List<AccountMetrics> metricsCollection) {
        Map<MetricType, MetricValue> aggregated = new EnumMap<>(MetricType.class);
        
        if (metricsCollection == null || metricsCollection.isEmpty()) {
            return aggregated;
        }
        
        // Initialize with zero values
        for (MetricType type : MetricType.values()) {
            aggregated.put(type, MetricValue.zero(type));
        }
        
        // Sum all metrics
        for (AccountMetrics metrics : metricsCollection) {
            for (Map.Entry<MetricType, MetricValue> entry : metrics.getMetrics().entrySet()) {
                MetricType type = entry.getKey();
                MetricValue value = entry.getValue();
                MetricValue current = aggregated.get(type);
                aggregated.put(type, current.add(value));
            }
        }
        
        return aggregated;
    }
    
    public Map<MetricType, MetricValue> calculateAverageMetrics(List<AccountMetrics> metricsCollection) {
        Map<MetricType, MetricValue> totals = aggregateMetrics(metricsCollection);
        Map<MetricType, MetricValue> averages = new EnumMap<>(MetricType.class);
        
        if (metricsCollection.isEmpty()) {
            return averages;
        }
        
        BigDecimal count = BigDecimal.valueOf(metricsCollection.size());
        
        for (Map.Entry<MetricType, MetricValue> entry : totals.entrySet()) {
            MetricType type = entry.getKey();
            MetricValue total = entry.getValue();
            MetricValue average = total.divide(count);
            averages.put(type, average);
        }
        
        return averages;
    }
    
    public MetricValue calculateGrowthRate(MetricValue current, MetricValue previous) {
        if (previous.isZero()) {
            // If previous was zero, return 100% if current is positive, 0% if current is also zero
            return current.isZero() ? 
                MetricValue.zero(MetricType.ENGAGEMENT_RATE) : 
                MetricValue.of(100.0, MetricType.ENGAGEMENT_RATE);
        }
        
        return current.growth(previous);
    }
    
    public double calculateEngagementRate(long likes, long comments, long shares, long saves, long views) {
        if (views == 0) {
            return 0.0;
        }
        
        long totalEngagement = likes + comments + shares + saves;
        return (double) totalEngagement / views * 100.0;
    }
    
    public double calculateReachEfficiency(long reach, long followers) {
        if (followers == 0) {
            return 0.0;
        }
        
        return (double) reach / followers * 100.0;
    }
    
    public boolean isHighPerformingPost(PostMetrics postMetrics) {
        return postMetrics.isHighPerforming();
    }
    
    public boolean isViralPost(PostMetrics postMetrics) {
        return postMetrics.isViralPost();
    }
    
    public MetricValue calculateAverageEngagementPerPost(AccountMetrics accountMetrics) {
        MetricValue totalPosts = accountMetrics.getMetric(MetricType.POSTS);
        if (totalPosts.isZero()) {
            return MetricValue.zero(MetricType.ENGAGEMENT_RATE);
        }
        
        long totalEngagement = accountMetrics.getTotalEngagement();
        return MetricValue.of(totalEngagement, MetricType.LIKES).divide(totalPosts.asBigDecimal());
    }
    
    public Map<MetricType, Double> calculateTrends(List<AccountMetrics> historicalMetrics, TimePeriod period) {
        Map<MetricType, Double> trends = new EnumMap<>(MetricType.class);
        
        if (historicalMetrics.size() < 2) {
            return trends; // Need at least 2 data points for trend
        }
        
        // Sort by period start (assuming they're for consecutive periods)
        historicalMetrics.sort((a, b) -> a.getPeriodStart().compareTo(b.getPeriodStart()));
        
        AccountMetrics oldest = historicalMetrics.get(0);
        AccountMetrics newest = historicalMetrics.get(historicalMetrics.size() - 1);
        
        for (MetricType type : MetricType.values()) {
            if (type.isCountMetric()) {
                MetricValue oldValue = oldest.getMetric(type);
                MetricValue newValue = newest.getMetric(type);
                MetricValue growth = calculateGrowthRate(newValue, oldValue);
                trends.put(type, growth.asDouble());
            }
        }
        
        return trends;
    }
    
    public double calculateContentEffectiveness(List<PostMetrics> postMetrics) {
        if (postMetrics.isEmpty()) {
            return 0.0;
        }
        
        double totalEngagementRate = postMetrics.stream()
            .mapToDouble(PostMetrics::getEngagementRateValue)
            .sum();
            
        return totalEngagementRate / postMetrics.size();
    }
    
    public Map<String, Object> generatePerformanceInsights(AccountMetrics metrics) {
        return Map.of(
            "isHighEngagement", metrics.getEngagementRateValue() > 3.0,
            "isGrowingFollowers", metrics.hasMetric(MetricType.FOLLOWERS),
            "totalEngagement", metrics.getTotalEngagement(),
            "engagementRate", metrics.getEngagementRateValue(),
            "followersCount", metrics.getMetric(MetricType.FOLLOWERS).asLong(),
            "postsCount", metrics.getMetric(MetricType.POSTS).asLong()
        );
    }
    
    public String getPerformanceCategory(double engagementRate) {
        if (engagementRate >= 6.0) {
            return "EXCELENTE";
        } else if (engagementRate >= 3.0) {
            return "BOM";
        } else if (engagementRate >= 1.0) {
            return "MEDIO";
        } else {
            return "BAIXO";
        }
    }
    
    public BigDecimal calculateROI(long investment, long returns) {
        if (investment == 0) {
            return BigDecimal.ZERO;
        }
        
        return BigDecimal.valueOf(returns)
            .subtract(BigDecimal.valueOf(investment))
            .divide(BigDecimal.valueOf(investment), 4, RoundingMode.HALF_UP)
            .multiply(BigDecimal.valueOf(100));
    }
}
