# services/tenant/app/services/network_alerts_service.py
"""
Network Alerts Service
Business logic for aggregating and managing alerts across enterprise networks
"""

from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
import uuid
import structlog

logger = structlog.get_logger()


class NetworkAlertsService:
    """
    Service for aggregating and managing alerts across enterprise networks
    """
    
    def __init__(self, tenant_client, alerts_client):
        self.tenant_client = tenant_client
        self.alerts_client = alerts_client
    
    async def get_child_tenants(self, parent_id: str) -> List[Dict[str, Any]]:
        """
        Get all child tenants for a parent tenant
        """
        try:
            # Get child tenants from tenant service
            children = await self.tenant_client.get_child_tenants(parent_id)
            
            # Enrich with tenant details
            enriched_children = []
            for child in children:
                child_details = await self.tenant_client.get_tenant(child['id'])
                enriched_children.append({
                    'id': child['id'],
                    'name': child_details.get('name', f"Outlet {child['id']}"),
                    'subdomain': child_details.get('subdomain'),
                    'city': child_details.get('city')
                })
            
            return enriched_children
            
        except Exception as e:
            logger.error(f"Failed to get child tenants, parent_id={parent_id}, error={str(e)}")
            raise Exception(f"Failed to get child tenants: {str(e)}")
    
    async def get_alerts_for_tenant(self, tenant_id: str) -> List[Dict[str, Any]]:
        """
        Get alerts for a specific tenant
        """
        try:
            # In a real implementation, this would call the alert service
            # For demo purposes, we'll simulate some alert data
            
            # Simulate different types of alerts based on tenant type
            simulated_alerts = []
            
            # Generate some sample alerts
            alert_types = ['inventory', 'production', 'delivery', 'equipment', 'quality']
            severities = ['critical', 'high', 'medium', 'low']
            
            for i in range(3):  # Generate 3 sample alerts per tenant
                alert = {
                    'alert_id': str(uuid.uuid4()),
                    'tenant_id': tenant_id,
                    'alert_type': alert_types[i % len(alert_types)],
                    'severity': severities[i % len(severities)],
                    'title': f"{alert_types[i % len(alert_types)].title()} Alert Detected",
                    'message': f"Sample {alert_types[i % len(alert_types)]} alert for tenant {tenant_id}",
                    'timestamp': (datetime.now() - timedelta(hours=i)).isoformat(),
                    'status': 'active' if i < 2 else 'resolved',
                    'source_system': f"{alert_types[i % len(alert_types)]}-service",
                    'related_entity_id': f"entity-{i+1}",
                    'related_entity_type': alert_types[i % len(alert_types)]
                }
                simulated_alerts.append(alert)
            
            return simulated_alerts
            
        except Exception as e:
            logger.error(f"Failed to get alerts for tenant, tenant_id={tenant_id}, error={str(e)}")
            raise Exception(f"Failed to get alerts: {str(e)}")
    
    async def get_network_alerts(self, parent_id: str) -> List[Dict[str, Any]]:
        """
        Get all alerts across the network
        """
        try:
            # Get all child tenants
            child_tenants = await self.get_child_tenants(parent_id)
            
            # Aggregate alerts from all child tenants
            all_alerts = []
            
            for child in child_tenants:
                child_id = child['id']
                child_alerts = await self.get_alerts_for_tenant(child_id)
                all_alerts.extend(child_alerts)
            
            return all_alerts
            
        except Exception as e:
            logger.error("Failed to get network alerts", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get network alerts: {str(e)}")
    
    async def detect_alert_correlations(
        self,
        alerts: List[Dict[str, Any]],
        min_correlation_strength: float = 0.7
    ) -> List[Dict[str, Any]]:
        """
        Detect correlations between alerts
        """
        try:
            # Simple correlation detection (in real implementation, this would be more sophisticated)
            correlations = []
            
            # Group alerts by type and time proximity
            alert_groups = {}
            
            for alert in alerts:
                alert_type = alert['alert_type']
                timestamp = alert['timestamp']
                
                # Use timestamp as key for grouping (simplified)
                if alert_type not in alert_groups:
                    alert_groups[alert_type] = []
                
                alert_groups[alert_type].append(alert)
            
            # Create correlation groups
            for alert_type, group in alert_groups.items():
                if len(group) >= 2:  # Only create correlations for groups with 2+ alerts
                    primary_alert = group[0]
                    related_alerts = group[1:]
                    
                    correlation = {
                        'correlation_id': str(uuid.uuid4()),
                        'primary_alert': primary_alert,
                        'related_alerts': related_alerts,
                        'correlation_type': 'temporal',
                        'correlation_strength': 0.85,
                        'impact_analysis': f"Multiple {alert_type} alerts detected within short timeframe"
                    }
                    
                    if correlation['correlation_strength'] >= min_correlation_strength:
                        correlations.append(correlation)
            
            return correlations
            
        except Exception as e:
            logger.error("Failed to detect alert correlations", error=str(e))
            raise Exception(f"Failed to detect correlations: {str(e)}")
    
    async def acknowledge_alert(self, parent_id: str, alert_id: str) -> Dict[str, Any]:
        """
        Acknowledge an alert
        """
        try:
            # In a real implementation, this would update the alert status
            # For demo purposes, we'll simulate the operation
            
            logger.info("Alert acknowledged", parent_id=parent_id, alert_id=alert_id)
            
            return {
                'success': True,
                'alert_id': alert_id,
                'status': 'acknowledged'
            }
            
        except Exception as e:
            logger.error("Failed to acknowledge alert", parent_id=parent_id, alert_id=alert_id, error=str(e))
            raise Exception(f"Failed to acknowledge alert: {str(e)}")
    
    async def resolve_alert(self, parent_id: str, alert_id: str, resolution_notes: Optional[str] = None) -> Dict[str, Any]:
        """
        Resolve an alert
        """
        try:
            # In a real implementation, this would update the alert status
            # For demo purposes, we'll simulate the operation
            
            logger.info("Alert resolved", parent_id=parent_id, alert_id=alert_id, notes=resolution_notes)
            
            return {
                'success': True,
                'alert_id': alert_id,
                'status': 'resolved',
                'resolution_notes': resolution_notes
            }
            
        except Exception as e:
            logger.error("Failed to resolve alert", parent_id=parent_id, alert_id=alert_id, error=str(e))
            raise Exception(f"Failed to resolve alert: {str(e)}")
    
    async def get_alert_trends(self, parent_id: str, days: int = 30) -> List[Dict[str, Any]]:
        """
        Get alert trends over time
        """
        try:
            # Simulate trend data
            trends = []
            end_date = datetime.now()
            
            # Generate daily trend data
            for i in range(days):
                date = end_date - timedelta(days=i)
                
                # Simulate varying alert counts with weekly pattern
                base_count = 5
                weekly_variation = int((i % 7) * 1.5)  # Higher on weekdays
                daily_noise = (i % 3 - 1)  # Daily noise
                
                alert_count = max(1, base_count + weekly_variation + daily_noise)
                
                trends.append({
                    'date': date.strftime('%Y-%m-%d'),
                    'total_alerts': alert_count,
                    'critical_alerts': max(0, int(alert_count * 0.1)),
                    'high_alerts': max(0, int(alert_count * 0.2)),
                    'medium_alerts': max(0, int(alert_count * 0.4)),
                    'low_alerts': max(0, int(alert_count * 0.3))
                })
            
            # Sort by date (oldest first)
            trends.sort(key=lambda x: x['date'])
            
            return trends
            
        except Exception as e:
            logger.error("Failed to get alert trends", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get alert trends: {str(e)}")
    
    async def get_prioritized_alerts(self, parent_id: str, limit: int = 10) -> List[Dict[str, Any]]:
        """
        Get prioritized alerts based on impact and urgency
        """
        try:
            # Get all network alerts
            all_alerts = await self.get_network_alerts(parent_id)
            
            if not all_alerts:
                return []
            
            # Simple prioritization (in real implementation, this would use ML)
            # Priority based on severity and recency
            severity_scores = {'critical': 4, 'high': 3, 'medium': 2, 'low': 1}
            
            for alert in all_alerts:
                severity_score = severity_scores.get(alert['severity'], 1)
                # Add recency score (newer alerts get higher priority)
                timestamp = datetime.fromisoformat(alert['timestamp'])
                recency_score = min(3, (datetime.now() - timestamp).days + 1)
                
                alert['priority_score'] = severity_score * recency_score
            
            # Sort by priority score (highest first)
            all_alerts.sort(key=lambda x: x['priority_score'], reverse=True)
            
            # Return top N alerts
            prioritized = all_alerts[:limit]
            
            # Remove priority score from response
            for alert in prioritized:
                alert.pop('priority_score', None)
            
            return prioritized
            
        except Exception as e:
            logger.error("Failed to get prioritized alerts", parent_id=parent_id, error=str(e))
            raise Exception(f"Failed to get prioritized alerts: {str(e)}")


# Helper class for alert analysis
class AlertAnalyzer:
    """
    Helper class for analyzing alert patterns
    """
    
    @staticmethod
    def calculate_alert_severity_score(alert: Dict[str, Any]) -> float:
        """
        Calculate severity score for an alert
        """
        severity_scores = {'critical': 1.0, 'high': 0.75, 'medium': 0.5, 'low': 0.25}
        return severity_scores.get(alert['severity'], 0.25)
    
    @staticmethod
    def detect_alert_patterns(alerts: List[Dict[str, Any]]) -> Dict[str, Any]:
        """
        Detect patterns in alert data
        """
        if not alerts:
            return {'patterns': [], 'anomalies': []}
        
        patterns = []
        anomalies = []
        
        # Simple pattern detection
        alert_types = [a['alert_type'] for a in alerts]
        type_counts = {}
        
        for alert_type in alert_types:
            type_counts[alert_type] = type_counts.get(alert_type, 0) + 1
        
        # Detect if one type dominates
        total_alerts = len(alerts)
        for alert_type, count in type_counts.items():
            if count / total_alerts > 0.6:  # More than 60% of one type
                patterns.append({
                    'type': 'dominant_alert_type',
                    'pattern': f'{alert_type} alerts dominate ({count}/{total_alerts})',
                    'confidence': 0.85
                })
        
        return {'patterns': patterns, 'anomalies': anomalies}


# Helper class for alert correlation
class AlertCorrelator:
    """
    Helper class for correlating alerts
    """
    
    @staticmethod
    def calculate_correlation_strength(alert1: Dict[str, Any], alert2: Dict[str, Any]) -> float:
        """
        Calculate correlation strength between two alerts
        """
        # Simple correlation based on type and time proximity
        same_type = 1.0 if alert1['alert_type'] == alert2['alert_type'] else 0.3
        
        time1 = datetime.fromisoformat(alert1['timestamp'])
        time2 = datetime.fromisoformat(alert2['timestamp'])
        time_diff_hours = abs((time2 - time1).total_seconds() / 3600)
        
        # Time proximity score (higher for closer times)
        time_proximity = max(0, 1.0 - min(1.0, time_diff_hours / 24))  # Decays over 24 hours
        
        return same_type * time_proximity


# Helper class for alert prioritization
class AlertPrioritizer:
    """
    Helper class for prioritizing alerts
    """
    
    @staticmethod
    def calculate_priority_score(alert: Dict[str, Any]) -> float:
        """
        Calculate priority score for an alert
        """
        # Base score from severity
        severity_scores = {'critical': 100, 'high': 75, 'medium': 50, 'low': 25}
        base_score = severity_scores.get(alert['severity'], 25)
        
        # Add recency bonus (newer alerts get higher priority)
        timestamp = datetime.fromisoformat(alert['timestamp'])
        hours_old = (datetime.now() - timestamp).total_seconds() / 3600
        recency_bonus = max(0, 50 - hours_old)  # Decays over 50 hours
        
        return base_score + recency_bonus