import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import random
from typing import Dict, Tuple

class UsageAnalyzer:
    def __init__(self, database_manager):
        self.db = database_manager
    
    def generate_daily_usage(self, total_usage_gb: float, date_from: datetime.date, date_to: datetime.date) -> Dict[str, float]:
        """
        Generate realistic daily usage distribution for the given period
        """
        # Calculate number of days
        num_days = (date_to - date_from).days + 1
        
        if num_days <= 0:
            return {}
        
        # Generate realistic usage pattern
        daily_usage = self._generate_realistic_pattern(total_usage_gb, num_days)
        
        # Create date-usage mapping
        usage_breakdown = {}
        current_date = date_from
        
        for i in range(num_days):
            date_str = current_date.strftime('%Y-%m-%d')
            usage_breakdown[date_str] = daily_usage[i]
            current_date += timedelta(days=1)
        
        return usage_breakdown
    
    def _generate_realistic_pattern(self, total_usage_gb: float, num_days: int) -> list:
        """
        Generate realistic daily usage pattern with variations
        """
        # Base patterns for different usage behaviors
        patterns = {
            'normal': self._normal_usage_pattern(num_days),
            'heavy_weekends': self._weekend_heavy_pattern(num_days),
            'business': self._business_usage_pattern(num_days),
            'evening_peak': self._evening_peak_pattern(num_days),
            'consistent': self._consistent_pattern(num_days)
        }
        
        # Randomly select a pattern or blend patterns
        pattern_type = random.choice(list(patterns.keys()))
        base_pattern = patterns[pattern_type]
        
        # Add some randomness and variation
        daily_factors = []
        for i in range(num_days):
            # Apply day-of-week effects
            day_of_week = i % 7
            
            # Weekend effect (Saturday=5, Sunday=6)
            if day_of_week in [5, 6]:
                weekend_factor = random.uniform(0.8, 1.4)
            else:
                weekend_factor = random.uniform(0.9, 1.2)
            
            # Random daily variation
            daily_variation = random.uniform(0.7, 1.5)
            
            # Combine factors
            factor = base_pattern[i] * weekend_factor * daily_variation
            daily_factors.append(factor)
        
        # Normalize to match total usage
        total_factor = sum(daily_factors)
        normalized_usage = [(factor / total_factor) * total_usage_gb for factor in daily_factors]
        
        # Ensure minimum usage per day (at least 1MB)
        min_usage = 0.001  # 1MB in GB
        for i in range(len(normalized_usage)):
            if normalized_usage[i] < min_usage:
                normalized_usage[i] = min_usage
        
        # Adjust to maintain total
        current_total = sum(normalized_usage)
        if current_total != total_usage_gb:
            adjustment_factor = total_usage_gb / current_total
            normalized_usage = [usage * adjustment_factor for usage in normalized_usage]
        
        return normalized_usage
    
    def _normal_usage_pattern(self, num_days: int) -> list:
        """Normal bell-curve distribution with slight variations"""
        pattern = []
        for i in range(num_days):
            # Create a slightly varying pattern
            base_value = 1.0 + 0.3 * np.sin(2 * np.pi * i / 7)  # Weekly cycle
            pattern.append(base_value)
        return pattern
    
    def _weekend_heavy_pattern(self, num_days: int) -> list:
        """Higher usage on weekends"""
        pattern = []
        for i in range(num_days):
            day_of_week = i % 7
            if day_of_week in [5, 6]:  # Weekend
                pattern.append(1.5)
            else:
                pattern.append(0.8)
        return pattern
    
    def _business_usage_pattern(self, num_days: int) -> list:
        """Higher usage on weekdays"""
        pattern = []
        for i in range(num_days):
            day_of_week = i % 7
            if day_of_week in [0, 1, 2, 3, 4]:  # Weekdays
                pattern.append(1.2)
            else:
                pattern.append(0.6)
        return pattern
    
    def _evening_peak_pattern(self, num_days: int) -> list:
        """Simulates evening peak usage with daily variations"""
        pattern = []
        for i in range(num_days):
            # Simulate daily variation with random peaks
            if random.random() < 0.3:  # 30% chance of peak day
                pattern.append(1.8)
            else:
                pattern.append(0.9)
        return pattern
    
    def _consistent_pattern(self, num_days: int) -> list:
        """Relatively consistent usage with minor variations"""
        return [random.uniform(0.95, 1.05) for _ in range(num_days)]
    
    def get_daily_usage_trend(self, days=30):
        """Get daily usage trend for dashboard"""
        return self.db.get_daily_usage_trend(days)
    
    def get_fup_status_distribution(self):
        """Get FUP status distribution"""
        return self.db.get_fup_status_distribution()
    
    def get_daily_breakdown(self, usage_record_id):
        """Get daily breakdown for a specific usage record"""
        return self.db.get_daily_breakdown(usage_record_id)
    
    def analyze_usage_patterns(self, client_id=None):
        """
        Analyze usage patterns for insights
        """
        if client_id:
            usage_records = self.db.get_client_usage_records(client_id)
        else:
            # Get all usage records
            with self.db.get_connection() as conn:
                usage_records = pd.read_sql_query("""
                    SELECT ur.*, c.client_name 
                    FROM usage_records ur
                    JOIN clients c ON ur.client_id = c.client_id
                    ORDER BY ur.date_from DESC
                """, conn)
        
        if usage_records.empty:
            return {}
        
        analysis = {
            'total_records': len(usage_records),
            'avg_usage_gb': usage_records['total_usage_gb'].mean(),
            'max_usage_gb': usage_records['total_usage_gb'].max(),
            'min_usage_gb': usage_records['total_usage_gb'].min(),
            'fup_violation_rate': (usage_records['fup_reached'].sum() / len(usage_records)) * 100,
            'usage_trend': 'stable'  # Could be enhanced with more sophisticated analysis
        }
        
        return analysis
    
    def get_usage_statistics(self):
        """Get comprehensive usage statistics"""
        with self.db.get_connection() as conn:
            # Overall statistics
            stats = pd.read_sql_query("""
                SELECT 
                    COUNT(*) as total_records,
                    COUNT(DISTINCT client_id) as unique_clients,
                    SUM(total_usage_gb) as total_usage,
                    AVG(total_usage_gb) as avg_usage,
                    MAX(total_usage_gb) as max_usage,
                    MIN(total_usage_gb) as min_usage,
                    SUM(CASE WHEN fup_reached = 1 THEN 1 ELSE 0 END) as fup_violations
                FROM usage_records
            """, conn)
            
            if not stats.empty:
                return stats.iloc[0].to_dict()
            else:
                return {
                    'total_records': 0,
                    'unique_clients': 0,
                    'total_usage': 0,
                    'avg_usage': 0,
                    'max_usage': 0,
                    'min_usage': 0,
                    'fup_violations': 0
                }
    
    def predict_future_usage(self, client_id, days_ahead=7):
        """
        Simple usage prediction based on historical patterns
        """
        usage_records = self.db.get_client_usage_records(client_id)
        
        if usage_records.empty:
            return None
        
        # Calculate average daily usage
        total_usage = usage_records['total_usage_gb'].sum()
        total_days = sum([(pd.to_datetime(row['date_to']) - pd.to_datetime(row['date_from'])).days + 1 
                         for _, row in usage_records.iterrows()])
        
        if total_days == 0:
            return None
        
        avg_daily_usage = total_usage / total_days
        
        # Predict future usage with some variation
        predicted_usage = []
        for i in range(days_ahead):
            # Add some randomness to the prediction
            variation = random.uniform(0.8, 1.2)
            daily_prediction = avg_daily_usage * variation
            predicted_usage.append(daily_prediction)
        
        return {
            'predicted_daily_usage': predicted_usage,
            'total_predicted_usage': sum(predicted_usage),
            'avg_daily_historical': avg_daily_usage,
            'confidence': 'medium'  # Simple confidence indicator
        }
