from typing import Dict, List, Optional
from datetime import datetime, timedelta
import logging

logger = logging.getLogger(__name__)

class ReportsEngine:
    """Generate network reports and analytics"""
    
    def __init__(self, db):
        self.db = db
    
    async def generate_daily_report(self, date: Optional[datetime] = None, org_id: str = None) -> Dict:
        """Generate daily network report"""
        if not date:
            date = datetime.utcnow()
        
        start_of_day = date.replace(hour=0, minute=0, second=0, microsecond=0)
        end_of_day = start_of_day + timedelta(days=1)
        
        org_filter = {"organization_id": org_id} if org_id else {}
        
        total_devices = await self.db.devices.count_documents(org_filter)
        online_devices = await self.db.devices.count_documents({**org_filter, 'status': 'online'})
        offline_devices = await self.db.devices.count_documents({**org_filter, 'status': 'offline'})
        
        alerts = await self.db.alerts.count_documents({
            **org_filter,
            'timestamp': {'$gte': start_of_day, '$lt': end_of_day}
        })
        critical_alerts = await self.db.alerts.count_documents({
            **org_filter,
            'timestamp': {'$gte': start_of_day, '$lt': end_of_day},
            'severity': 'critical'
        })
        
        scans = await self.db.scans.count_documents({
            **org_filter,
            'started_at': {'$gte': start_of_day, '$lt': end_of_day}
        })
        
        new_devices = await self.db.devices.count_documents({
            **org_filter,
            'first_discovered': {'$gte': start_of_day, '$lt': end_of_day}
        })
        
        pipeline = [
            {'$match': {**org_filter, 'timestamp': {'$gte': start_of_day, '$lt': end_of_day}}},
            {'$group': {'_id': '$device_id', 'count': {'$sum': 1}}},
            {'$sort': {'count': -1}},
            {'$limit': 10}
        ]
        top_alert_devices = await self.db.alerts.aggregate(pipeline).to_list(10)
        
        return {
            'date': start_of_day.isoformat(),
            'summary': {
                'total_devices': total_devices,
                'online_devices': online_devices,
                'offline_devices': offline_devices,
                'availability_percentage': (online_devices / total_devices * 100) if total_devices > 0 else 0,
            },
            'alerts': {
                'total': alerts,
                'critical': critical_alerts,
            },
            'activity': {
                'scans_performed': scans,
                'new_devices': new_devices,
            },
            'top_alert_devices': top_alert_devices,
        }
    
    async def generate_weekly_report(self, end_date: Optional[datetime] = None, org_id: str = None) -> Dict:
        """Generate weekly network report"""
        if not end_date:
            end_date = datetime.utcnow()
        
        start_date = end_date - timedelta(days=7)
        
        daily_stats = []
        current_date = start_date
        
        while current_date < end_date:
            day_report = await self.generate_daily_report(current_date, org_id=org_id)
            daily_stats.append(day_report)
            current_date += timedelta(days=1)
        
        # Weekly aggregates
        total_alerts = sum(day['alerts']['total'] for day in daily_stats)
        total_critical = sum(day['alerts']['critical'] for day in daily_stats)
        total_new_devices = sum(day['activity']['new_devices'] for day in daily_stats)
        
        # Average availability
        avg_availability = sum(day['summary']['availability_percentage'] for day in daily_stats) / len(daily_stats) if daily_stats else 0
        
        return {
            'period': {
                'start': start_date.isoformat(),
                'end': end_date.isoformat(),
            },
            'summary': {
                'total_alerts': total_alerts,
                'critical_alerts': total_critical,
                'new_devices': total_new_devices,
                'average_availability': avg_availability,
            },
            'daily_breakdown': daily_stats,
        }
    
    async def generate_device_report(self, device_id: str, days: int = 30, org_id: str = None) -> Dict:
        """Generate report for specific device scoped to organization"""
        query = {'id': device_id}
        if org_id:
            query['organization_id'] = org_id
        device = await self.db.devices.find_one(query)
        if not device:
            return {'error': 'Device not found'}
        
        end_date = datetime.utcnow()
        start_date = end_date - timedelta(days=days)
        
        # Get metrics history
        metrics = await self.db.device_metrics.find({
            'device_id': device_id,
            'timestamp': {'$gte': start_date}
        }).sort('timestamp', 1).to_list(10000)
        
        # Get alerts
        alerts = await self.db.alerts.find({
            'device_id': device_id,
            'timestamp': {'$gte': start_date}
        }).sort('timestamp', -1).to_list(1000)
        
        # Calculate uptime
        uptime_percentage = await self._calculate_uptime(device_id, start_date, end_date)
        
        # Average metrics
        avg_cpu = self._calculate_average([m.get('cpu') for m in metrics if m.get('cpu')])
        avg_memory = self._calculate_average([m.get('memory', {}).get('percentage') for m in metrics if m.get('memory', {}).get('percentage')])
        avg_temp = self._calculate_average([m.get('temperature') for m in metrics if m.get('temperature')])
        
        return {
            'device_id': device_id,
            'device_name': device.get('hostname', device['ip']),
            'ip': device['ip'],
            'vendor': device.get('vendor', 'Unknown'),
            'period': {
                'start': start_date.isoformat(),
                'end': end_date.isoformat(),
                'days': days,
            },
            'uptime': {
                'percentage': uptime_percentage,
            },
            'performance': {
                'average_cpu': avg_cpu,
                'average_memory': avg_memory,
                'average_temperature': avg_temp,
            },
            'alerts': {
                'total': len(alerts),
                'critical': len([a for a in alerts if a.get('severity') == 'critical']),
                'warning': len([a for a in alerts if a.get('severity') == 'warning']),
            },
            'metrics_count': len(metrics),
        }
    
    async def _calculate_uptime(self, device_id: str, start_date: datetime, end_date: datetime) -> float:
        """Calculate device uptime percentage"""
        metrics = await self.db.device_metrics.find({
            'device_id': device_id,
            'timestamp': {'$gte': start_date, '$lte': end_date}
        }).to_list(10000)
        
        if not metrics:
            return 0.0
        
        # Count as up if we have metrics
        total_time = (end_date - start_date).total_seconds()
        # Assume 1 metric per minute means device was up
        up_time = len(metrics) * 60  # seconds
        
        return min(100.0, (up_time / total_time) * 100) if total_time > 0 else 0.0
    
    def _calculate_average(self, values: List) -> Optional[float]:
        """Calculate average of numeric values"""
        valid_values = [v for v in values if v is not None]
        if not valid_values:
            return None
        return sum(valid_values) / len(valid_values)
    
    async def generate_vendor_report(self, org_id: str = None) -> Dict:
        """Generate report by vendor"""
        match_stage = [{"$match": {"organization_id": org_id}}] if org_id else []
        pipeline = match_stage + [
            {'$group': {
                '_id': '$vendor',
                'count': {'$sum': 1},
                'online': {'$sum': {'$cond': [{'$eq': ['$status', 'online']}, 1, 0]}},
                'offline': {'$sum': {'$cond': [{'$eq': ['$status', 'offline']}, 1, 0]}},
            }},
            {'$sort': {'count': -1}}
        ]
        
        vendor_stats = await self.db.devices.aggregate(pipeline).to_list(100)
        
        return {
            'generated_at': datetime.utcnow().isoformat(),
            'vendors': [
                {
                    'vendor': v['_id'] or 'Unknown',
                    'total_devices': v['count'],
                    'online': v['online'],
                    'offline': v['offline'],
                    'availability': (v['online'] / v['count'] * 100) if v['count'] > 0 else 0
                }
                for v in vendor_stats
            ]
        }
