from typing import Dict, List, Optional, Any, Callable, Union
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
import asyncio
import logging
from abc import ABC, abstractmethod

from .base_resource import BaseResource, ResourceState, DriftAction
from .resource_tracker import ResourceTracker, ResourceOperation, ResourceSnapshot
from ..exceptions import NexusException


logger = logging.getLogger(__name__)


class IntelligenceLevel(Enum):
    """Intelligence feature levels"""

    BASIC = "basic"  # Basic monitoring
    PREDICTIVE = "predictive"  # Predictive analytics
    ADAPTIVE = "adaptive"  # Adaptive responses
    AUTONOMOUS = "autonomous"  # Autonomous operations


class EventType(Enum):
    """Types of intelligence events"""

    RESOURCE_CREATED = "resource_created"
    RESOURCE_UPDATED = "resource_updated"
    RESOURCE_DELETED = "resource_deleted"
    RESOURCE_FAILED = "resource_failed"
    DRIFT_DETECTED = "drift_detected"
    DRIFT_RECONCILED = "drift_reconciled"
    COST_ANOMALY = "cost_anomaly"
    PERFORMANCE_DEGRADED = "performance_degraded"
    SECURITY_ALERT = "security_alert"
    CAPACITY_WARNING = "capacity_warning"
    POLICY_VIOLATION = "policy_violation"


class AlertSeverity(Enum):
    """Alert severity levels"""

    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


@dataclass
class IntelligenceEvent:
    """Event generated by intelligence features"""

    event_type: EventType
    resource_id: str
    timestamp: datetime
    severity: AlertSeverity
    title: str
    message: str
    data: Dict[str, Any] = field(default_factory=dict)
    source: str = "intelligence"
    resolved: bool = False
    resolved_at: Optional[datetime] = None
    actions_taken: List[str] = field(default_factory=list)


@dataclass
class IntelligenceRule:
    """Rule for intelligent behavior"""

    name: str
    condition: Callable[[Dict[str, Any]], bool]
    action: Callable[[Dict[str, Any]], Any]
    enabled: bool = True
    cooldown_seconds: int = 300  # 5 minutes
    last_triggered: Optional[datetime] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


class IntelligenceHook(ABC):
    """Base class for intelligence hooks"""

    def __init__(self, name: str):
        self.name = name
        self.enabled = True

    @abstractmethod
    async def execute(self, event: IntelligenceEvent, context: Dict[str, Any]) -> None:
        """Execute the hook"""
        pass

    def should_execute(self, event: IntelligenceEvent) -> bool:
        """Check if hook should execute for this event"""
        return self.enabled


class DriftDetectionHook(IntelligenceHook):
    """Hook for drift detection and remediation"""

    def __init__(self, auto_remediate: bool = False):
        super().__init__("drift_detection")
        self.auto_remediate = auto_remediate

    async def execute(self, event: IntelligenceEvent, context: Dict[str, Any]) -> None:
        if event.event_type == EventType.DRIFT_DETECTED:
            resource = context.get("resource")
            if resource and self.auto_remediate:
                # Attempt automatic remediation
                try:
                    if resource._drift_policy == DriftAction.REVERT:
                        await asyncio.to_thread(resource.create)
                        logger.info(
                            f"Automatically remediated drift for {resource.metadata.name}"
                        )
                except Exception as e:
                    logger.error(f"Failed to auto-remediate drift: {e}")


class CostOptimizationHook(IntelligenceHook):
    """Hook for cost optimization suggestions"""

    def __init__(self, cost_threshold: float = 100.0):
        super().__init__("cost_optimization")
        self.cost_threshold = cost_threshold

    async def execute(self, event: IntelligenceEvent, context: Dict[str, Any]) -> None:
        if event.event_type == EventType.COST_ANOMALY:
            # Generate cost optimization suggestions
            suggestions = self._generate_cost_suggestions(event.data)
            event.data["suggestions"] = suggestions

    def _generate_cost_suggestions(self, data: Dict[str, Any]) -> List[str]:
        """Generate cost optimization suggestions"""
        suggestions = []

        # Example suggestions based on resource type and usage
        if data.get("resource_type") == "instance":
            if data.get("cpu_utilization", 0) < 10:
                suggestions.append(
                    "Consider downsizing instance due to low CPU utilization"
                )
            if data.get("unused_days", 0) > 7:
                suggestions.append("Instance appears unused - consider termination")

        return suggestions


class SecurityScanHook(IntelligenceHook):
    """Hook for security scanning and alerts"""

    def __init__(self):
        super().__init__("security_scan")

    async def execute(self, event: IntelligenceEvent, context: Dict[str, Any]) -> None:
        if event.event_type in [EventType.RESOURCE_CREATED, EventType.RESOURCE_UPDATED]:
            resource = context.get("resource")
            if resource:
                # Perform security scan
                security_issues = await self._scan_resource(resource)
                if security_issues:
                    # Generate security alert
                    security_event = IntelligenceEvent(
                        event_type=EventType.SECURITY_ALERT,
                        resource_id=resource.metadata.id,
                        timestamp=datetime.utcnow(),
                        severity=AlertSeverity.HIGH,
                        title="Security Issues Detected",
                        message=f"Found {len(security_issues)} security issues",
                        data={"issues": security_issues},
                    )
                    context["intelligence_engine"].emit_event(security_event)

    async def _scan_resource(self, resource: BaseResource) -> List[Dict[str, Any]]:
        """Scan resource for security issues"""
        issues = []

        # Example security checks
        tags = resource.metadata.to_tags()
        if "public" in tags.get("network", "").lower():
            issues.append(
                {
                    "type": "network_exposure",
                    "severity": "high",
                    "message": "Resource may be publicly accessible",
                }
            )

        return issues


class IntelligenceEngine:
    """
    Central intelligence engine that provides predictive and adaptive capabilities.

    Features:
    - Event-driven architecture
    - Pluggable hooks and analyzers
    - Drift detection and auto-remediation
    - Cost optimization suggestions
    - Security scanning
    - Performance monitoring
    - Predictive analytics
    """

    def __init__(
        self,
        tracker: ResourceTracker,
        intelligence_level: IntelligenceLevel = IntelligenceLevel.BASIC,
    ):
        self.tracker = tracker
        self.intelligence_level = intelligence_level
        self._hooks: List[IntelligenceHook] = []
        self._rules: List[IntelligenceRule] = []
        self._event_handlers: Dict[EventType, List[Callable[..., Any]]] = {}
        self._events: List[IntelligenceEvent] = []
        self._analyzers: List[Callable[..., Any]] = []

        # Configuration
        self._max_events = 10000
        self._event_retention_days = 30

        # Built-in hooks
        self._register_builtin_hooks()

        # Background tasks
        self._analysis_task: Optional[asyncio.Task[None]] = None
        self._start_background_analysis()

    def _register_builtin_hooks(self) -> None:
        """Register built-in intelligence hooks"""
        self.add_hook(DriftDetectionHook(auto_remediate=True))
        self.add_hook(CostOptimizationHook())
        self.add_hook(SecurityScanHook())

    def add_hook(self, hook: IntelligenceHook) -> None:
        """Add an intelligence hook"""
        self._hooks.append(hook)
        logger.info(f"Added intelligence hook: {hook.name}")

    def remove_hook(self, hook_name: str) -> None:
        """Remove an intelligence hook"""
        self._hooks = [h for h in self._hooks if h.name != hook_name]
        logger.info(f"Removed intelligence hook: {hook_name}")

    def add_rule(self, rule: IntelligenceRule) -> None:
        """Add an intelligence rule"""
        self._rules.append(rule)
        logger.info(f"Added intelligence rule: {rule.name}")

    def on_event(self, event_type: EventType, handler: Callable[..., Any]) -> None:
        """Register an event handler"""
        if event_type not in self._event_handlers:
            self._event_handlers[event_type] = []
        self._event_handlers[event_type].append(handler)

    def emit_event(self, event: IntelligenceEvent) -> None:
        """Emit an intelligence event"""
        self._events.append(event)

        # Limit event history
        if len(self._events) > self._max_events:
            self._events = self._events[-self._max_events :]

        # Execute hooks
        self._execute_hooks(event)

        # Execute event handlers
        self._execute_event_handlers(event)

        # Evaluate rules
        self._evaluate_rules(event)

        logger.info(f"Intelligence event: {event.event_type.value} - {event.title}")

    def _execute_hooks(self, event: IntelligenceEvent) -> None:
        """Execute intelligence hooks for an event"""
        for hook in self._hooks:
            if hook.should_execute(event):
                try:
                    context: Dict[str, Any] = {
                        "intelligence_engine": self,
                        "tracker": self.tracker,
                        "event": event,
                    }

                    # Find resource if needed
                    if event.resource_id:
                        resource_metadata = self.tracker.get_resource_metadata(
                            event.resource_id
                        )
                        if resource_metadata:
                            context["resource_metadata"] = resource_metadata

                    # Execute hook asynchronously
                    asyncio.create_task(hook.execute(event, context))

                except Exception as e:
                    logger.error(f"Error executing hook {hook.name}: {e}")

    def _execute_event_handlers(self, event: IntelligenceEvent) -> None:
        """Execute registered event handlers"""
        handlers = self._event_handlers.get(event.event_type, [])

        for handler in handlers:
            try:
                handler(event)
            except Exception as e:
                logger.error(f"Error in event handler: {e}")

    def _evaluate_rules(self, event: IntelligenceEvent) -> None:
        """Evaluate intelligence rules"""
        for rule in self._rules:
            if not rule.enabled:
                continue

            # Check cooldown
            if (
                rule.last_triggered
                and datetime.utcnow() - rule.last_triggered
                < timedelta(seconds=rule.cooldown_seconds)
            ):
                continue

            try:
                # Evaluate condition
                context = {
                    "event": event,
                    "resource_id": event.resource_id,
                    "severity": event.severity,
                    "data": event.data,
                }

                if rule.condition(context):
                    # Execute action
                    rule.action(context)
                    rule.last_triggered = datetime.utcnow()

                    event.actions_taken.append(f"rule:{rule.name}")

            except Exception as e:
                logger.error(f"Error evaluating rule {rule.name}: {e}")

    def _start_background_analysis(self) -> None:
        """Start background analysis task"""
        if self.intelligence_level in [
            IntelligenceLevel.PREDICTIVE,
            IntelligenceLevel.ADAPTIVE,
            IntelligenceLevel.AUTONOMOUS,
        ]:
            self._analysis_task = asyncio.create_task(self._analysis_loop())

    async def _analysis_loop(self) -> None:
        """Background analysis loop"""
        while True:
            try:
                await self._perform_analysis()
                await asyncio.sleep(300)  # Run every 5 minutes
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Error in analysis loop: {e}")
                await asyncio.sleep(60)  # Wait before retry

    async def _perform_analysis(self) -> None:
        """Perform intelligent analysis"""
        # Get all tracked resources
        resources = self.tracker.get_all_resources()

        # Run analyzers
        for analyzer in self._analyzers:
            try:
                await analyzer(resources, self)
            except Exception as e:
                logger.error(f"Error in analyzer: {e}")

        # Predictive analysis
        if self.intelligence_level in [
            IntelligenceLevel.PREDICTIVE,
            IntelligenceLevel.ADAPTIVE,
            IntelligenceLevel.AUTONOMOUS,
        ]:
            await self._predictive_analysis(resources)

        # Adaptive responses
        if self.intelligence_level in [
            IntelligenceLevel.ADAPTIVE,
            IntelligenceLevel.AUTONOMOUS,
        ]:
            await self._adaptive_responses()

    async def _predictive_analysis(self, resources: List[Any]) -> None:
        """Perform predictive analysis"""
        # Analyze trends and predict issues
        for resource_metadata in resources:
            resource_id = resource_metadata.id

            # Get recent snapshots
            snapshots = self.tracker.get_snapshots(resource_id, limit=10)

            if len(snapshots) < 3:
                continue

            # Analyze cost trends
            cost_trend = self._analyze_cost_trend(snapshots)
            if cost_trend.get("anomaly"):
                self.emit_event(
                    IntelligenceEvent(
                        event_type=EventType.COST_ANOMALY,
                        resource_id=resource_id,
                        timestamp=datetime.utcnow(),
                        severity=AlertSeverity.MEDIUM,
                        title="Cost Anomaly Detected",
                        message=f"Unusual cost pattern detected: {cost_trend['description']}",
                        data=cost_trend,
                    )
                )

    async def _adaptive_responses(self) -> None:
        """Implement adaptive responses to events"""
        # Find unresolved high-severity events
        unresolved_events = [
            event
            for event in self._events
            if not event.resolved
            and event.severity in [AlertSeverity.HIGH, AlertSeverity.CRITICAL]
        ]

        for event in unresolved_events:
            # Implement adaptive response based on event type
            if event.event_type == EventType.DRIFT_DETECTED:
                await self._handle_drift_adaptively(event)
            elif event.event_type == EventType.PERFORMANCE_DEGRADED:
                await self._handle_performance_adaptively(event)

    async def _handle_drift_adaptively(self, event: IntelligenceEvent) -> None:
        """Handle drift with adaptive response"""
        # Implement intelligent drift handling
        pass

    async def _handle_performance_adaptively(self, event: IntelligenceEvent) -> None:
        """Handle performance issues with adaptive response"""
        # Implement intelligent performance response
        pass

    def _analyze_cost_trend(self, snapshots: List[ResourceSnapshot]) -> Dict[str, Any]:
        """Analyze cost trends from snapshots"""
        if not snapshots:
            return {"anomaly": False}

        # Simple cost trend analysis
        costs = [s.cost_estimate for s in snapshots if s.cost_estimate]

        if len(costs) < 3:
            return {"anomaly": False}

        # Check for significant increase
        recent_cost = costs[-1]
        baseline_cost = sum(costs[:-1]) / len(costs[:-1])

        if recent_cost > baseline_cost * 1.5:  # 50% increase
            return {
                "anomaly": True,
                "description": f"Cost increased by {((recent_cost / baseline_cost) - 1) * 100:.1f}%",
                "current_cost": recent_cost,
                "baseline_cost": baseline_cost,
            }

        return {"anomaly": False}

    def get_events(
        self,
        limit: Optional[int] = None,
        event_type: Optional[EventType] = None,
        severity: Optional[AlertSeverity] = None,
    ) -> List[IntelligenceEvent]:
        """Get intelligence events"""
        events = self._events

        # Filter by type
        if event_type:
            events = [e for e in events if e.event_type == event_type]

        # Filter by severity
        if severity:
            events = [e for e in events if e.severity == severity]

        # Sort by timestamp (newest first)
        events.sort(key=lambda e: e.timestamp, reverse=True)

        # Apply limit
        if limit:
            events = events[:limit]

        return events

    def resolve_event(self, event_id: str) -> None:
        """Mark an event as resolved"""
        for event in self._events:
            if event.resource_id == event_id:  # Simplified ID matching
                event.resolved = True
                event.resolved_at = datetime.utcnow()
                break

    def get_stats(self) -> Dict[str, Any]:
        """Get intelligence statistics"""
        total_events = len(self._events)
        resolved_events = sum(1 for e in self._events if e.resolved)

        events_by_type = {}
        events_by_severity = {}

        for event in self._events:
            # Count by type
            event_type = event.event_type.value
            events_by_type[event_type] = events_by_type.get(event_type, 0) + 1

            # Count by severity
            severity = event.severity.value
            events_by_severity[severity] = events_by_severity.get(severity, 0) + 1

        return {
            "intelligence_level": self.intelligence_level.value,
            "total_events": total_events,
            "resolved_events": resolved_events,
            "unresolved_events": total_events - resolved_events,
            "events_by_type": events_by_type,
            "events_by_severity": events_by_severity,
            "active_hooks": len(self._hooks),
            "active_rules": len([r for r in self._rules if r.enabled]),
        }

    def shutdown(self) -> None:
        """Shutdown the intelligence engine"""
        if self._analysis_task:
            self._analysis_task.cancel()
        logger.info("Intelligence engine shutdown")
