"""
AIVA Self-Monitoring Dashboard - PM-030

AIVA monitors her own health metrics.
Tracks memory usage, queue depth, success rate, and alerts on anomalies.
"""

import os
import json
import logging
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, asdict, field
from collections import deque
from pathlib import Path

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


@dataclass
class HealthMetric:
    """A single health metric measurement."""
    name: str
    value: float
    unit: str
    timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
    status: str = "healthy"  # healthy, warning, critical
    threshold_warning: Optional[float] = None
    threshold_critical: Optional[float] = None


@dataclass
class Alert:
    """An alert triggered by anomaly detection."""
    alert_id: str
    severity: str  # info, warning, critical
    metric_name: str
    message: str
    value: float
    threshold: float
    timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
    acknowledged: bool = False


class MetricsCollector:
    """Collects various system metrics."""

    def collect_memory_metrics(self) -> Dict[str, HealthMetric]:
        """Collect memory-related metrics."""
        try:
            import psutil
            mem = psutil.virtual_memory()
            return {
                "memory_percent": HealthMetric(
                    name="memory_percent",
                    value=mem.percent,
                    unit="%",
                    threshold_warning=80.0,
                    threshold_critical=95.0
                ),
                "memory_available_gb": HealthMetric(
                    name="memory_available_gb",
                    value=mem.available / (1024**3),
                    unit="GB",
                    threshold_warning=2.0,
                    threshold_critical=0.5
                )
            }
        except ImportError:
            return {
                "memory_percent": HealthMetric(
                    name="memory_percent",
                    value=-1,
                    unit="%",
                    status="unknown"
                )
            }

    def collect_cpu_metrics(self) -> Dict[str, HealthMetric]:
        """Collect CPU-related metrics."""
        try:
            import psutil
            return {
                "cpu_percent": HealthMetric(
                    name="cpu_percent",
                    value=psutil.cpu_percent(interval=0.1),
                    unit="%",
                    threshold_warning=80.0,
                    threshold_critical=95.0
                )
            }
        except ImportError:
            return {
                "cpu_percent": HealthMetric(
                    name="cpu_percent",
                    value=-1,
                    unit="%",
                    status="unknown"
                )
            }

    def collect_disk_metrics(self) -> Dict[str, HealthMetric]:
        """Collect disk usage metrics."""
        try:
            import psutil
            disk = psutil.disk_usage('/')
            return {
                "disk_percent": HealthMetric(
                    name="disk_percent",
                    value=disk.percent,
                    unit="%",
                    threshold_warning=80.0,
                    threshold_critical=95.0
                )
            }
        except ImportError:
            return {}


class QueueMonitor:
    """Monitors task queue depth."""

    def __init__(self, task_consumer=None):
        self.task_consumer = task_consumer

    def get_queue_depth(self) -> HealthMetric:
        """Get current queue depth."""
        depth = 0
        if self.task_consumer:
            try:
                status = self.task_consumer.get_status()
                depth = sum(status.get("queue_depth", {}).values())
            except Exception:
                pass

        return HealthMetric(
            name="queue_depth",
            value=depth,
            unit="tasks",
            threshold_warning=100,
            threshold_critical=500
        )


class SuccessRateTracker:
    """Tracks task success rate over time."""

    def __init__(self, window_size: int = 100):
        self.window_size = window_size
        self.results: deque = deque(maxlen=window_size)

    def record(self, success: bool) -> None:
        """Record a task result."""
        self.results.append(1 if success else 0)

    def get_success_rate(self) -> HealthMetric:
        """Get current success rate."""
        if not self.results:
            rate = 1.0  # No data = assume healthy
        else:
            rate = sum(self.results) / len(self.results)

        return HealthMetric(
            name="success_rate",
            value=rate * 100,
            unit="%",
            threshold_warning=80.0,
            threshold_critical=50.0
        )


class MonitoringDashboard:
    """
    AIVA's self-monitoring dashboard.

    Usage:
        dashboard = MonitoringDashboard()
        status = dashboard.health_status()
        if status["overall_status"] != "healthy":
            handle_unhealthy(status)
    """

    def __init__(
        self,
        task_consumer=None,
        rank_tracker=None,
        memory_bridge=None,
        log_dir: str = "logs"
    ):
        """
        Initialize the monitoring dashboard.

        Args:
            task_consumer: TaskConsumer for queue monitoring
            rank_tracker: RankTracker for rank metrics
            memory_bridge: MemoryBridge for memory stats
            log_dir: Directory for metrics logs
        """
        self.task_consumer = task_consumer
        self.rank_tracker = rank_tracker
        self.memory_bridge = memory_bridge
        self.log_dir = Path(log_dir)
        self.log_dir.mkdir(parents=True, exist_ok=True)

        self.metrics_collector = MetricsCollector()
        self.queue_monitor = QueueMonitor(task_consumer)
        self.success_tracker = SuccessRateTracker()

        self.alerts: List[Alert] = []
        self.metrics_history: deque = deque(maxlen=1000)

        logger.info("MonitoringDashboard initialized")

    def health_status(self) -> Dict[str, Any]:
        """
        Get comprehensive health status.

        Returns:
            Dict with all health metrics and overall status
        """
        metrics = {}
        alerts = []

        # Collect system metrics
        metrics.update(self.metrics_collector.collect_memory_metrics())
        metrics.update(self.metrics_collector.collect_cpu_metrics())
        metrics.update(self.metrics_collector.collect_disk_metrics())

        # Collect queue metrics
        metrics["queue_depth"] = self.queue_monitor.get_queue_depth()

        # Collect success rate
        metrics["success_rate"] = self.success_tracker.get_success_rate()

        # Check for rank metrics
        if self.rank_tracker:
            metrics["current_rank"] = HealthMetric(
                name="current_rank",
                value=self.rank_tracker.current_rank.value,
                unit="level"
            )
            metrics["tasks_completed"] = HealthMetric(
                name="tasks_completed",
                value=self.rank_tracker.metrics.tasks_completed,
                unit="tasks"
            )

        # Evaluate thresholds and create alerts
        overall_status = "healthy"
        for name, metric in metrics.items():
            if isinstance(metric, HealthMetric):
                status = self._evaluate_metric(metric)
                metric.status = status

                if status == "critical":
                    overall_status = "critical"
                    alerts.append(self._create_alert(metric, "critical"))
                elif status == "warning" and overall_status != "critical":
                    overall_status = "warning"
                    alerts.append(self._create_alert(metric, "warning"))

        # Store metrics history
        self.metrics_history.append({
            "timestamp": datetime.utcnow().isoformat(),
            "metrics": {k: asdict(v) for k, v in metrics.items() if isinstance(v, HealthMetric)}
        })

        # Store new alerts
        self.alerts.extend(alerts)

        return {
            "timestamp": datetime.utcnow().isoformat(),
            "overall_status": overall_status,
            "metrics": {k: asdict(v) if isinstance(v, HealthMetric) else v for k, v in metrics.items()},
            "new_alerts": [asdict(a) for a in alerts],
            "active_alerts": len([a for a in self.alerts if not a.acknowledged])
        }

    def _evaluate_metric(self, metric: HealthMetric) -> str:
        """Evaluate metric against thresholds."""
        if metric.threshold_critical is not None:
            # For most metrics, higher is worse
            if metric.name in ["memory_available_gb", "success_rate"]:
                # Lower is worse for these
                if metric.value < metric.threshold_critical:
                    return "critical"
                if metric.threshold_warning and metric.value < metric.threshold_warning:
                    return "warning"
            else:
                # Higher is worse
                if metric.value > metric.threshold_critical:
                    return "critical"
                if metric.threshold_warning and metric.value > metric.threshold_warning:
                    return "warning"

        return "healthy"

    def _create_alert(self, metric: HealthMetric, severity: str) -> Alert:
        """Create an alert for a metric."""
        threshold = metric.threshold_critical if severity == "critical" else metric.threshold_warning
        return Alert(
            alert_id=f"alert_{int(time.time() * 1000)}",
            severity=severity,
            metric_name=metric.name,
            message=f"{metric.name} is {severity}: {metric.value:.1f}{metric.unit}",
            value=metric.value,
            threshold=threshold or 0.0
        )

    def record_task_result(self, success: bool) -> None:
        """Record a task result for success rate tracking."""
        self.success_tracker.record(success)

    def get_alerts(self, include_acknowledged: bool = False) -> List[Dict]:
        """Get current alerts."""
        alerts = self.alerts if include_acknowledged else [a for a in self.alerts if not a.acknowledged]
        return [asdict(a) for a in alerts]

    def acknowledge_alert(self, alert_id: str) -> bool:
        """Acknowledge an alert."""
        for alert in self.alerts:
            if alert.alert_id == alert_id:
                alert.acknowledged = True
                return True
        return False

    def get_metrics_trend(self, metric_name: str, minutes: int = 60) -> List[Dict]:
        """
        Get trend data for a metric.

        Args:
            metric_name: Name of the metric
            minutes: How many minutes of history

        Returns:
            List of {timestamp, value} dicts
        """
        cutoff = datetime.utcnow() - timedelta(minutes=minutes)
        trend = []

        for entry in self.metrics_history:
            entry_time = datetime.fromisoformat(entry["timestamp"])
            if entry_time >= cutoff:
                if metric_name in entry["metrics"]:
                    trend.append({
                        "timestamp": entry["timestamp"],
                        "value": entry["metrics"][metric_name]["value"]
                    })

        return trend

    def get_dashboard_summary(self) -> Dict:
        """Get a summary for dashboard display."""
        status = self.health_status()

        return {
            "overall_status": status["overall_status"],
            "key_metrics": {
                "memory_percent": status["metrics"].get("memory_percent", {}).get("value", -1),
                "cpu_percent": status["metrics"].get("cpu_percent", {}).get("value", -1),
                "queue_depth": status["metrics"].get("queue_depth", {}).get("value", 0),
                "success_rate": status["metrics"].get("success_rate", {}).get("value", 100)
            },
            "active_alerts": status["active_alerts"],
            "last_updated": status["timestamp"]
        }

    def export_metrics(self, path: str) -> bool:
        """Export metrics history to file."""
        try:
            with open(path, "w") as f:
                json.dump(list(self.metrics_history), f, indent=2)
            return True
        except Exception as e:
            logger.error(f"Failed to export metrics: {e}")
            return False


# Singleton instance
_dashboard: Optional[MonitoringDashboard] = None


def get_monitoring_dashboard(**kwargs) -> MonitoringDashboard:
    """Get or create singleton MonitoringDashboard."""
    global _dashboard
    if _dashboard is None:
        _dashboard = MonitoringDashboard(**kwargs)
    return _dashboard


if __name__ == "__main__":
    # Example usage
    dashboard = MonitoringDashboard()

    # Get health status
    status = dashboard.health_status()

    print("\n=== AIVA Health Status ===")
    print(f"Overall Status: {status['overall_status'].upper()}")
    print(f"\nMetrics:")
    for name, metric in status["metrics"].items():
        if isinstance(metric, dict):
            status_indicator = {"healthy": "[OK]", "warning": "[!]", "critical": "[X]"}.get(metric.get("status", "healthy"), "[ ]")
            print(f"  {status_indicator} {name}: {metric.get('value', 'N/A')}{metric.get('unit', '')}")

    if status["new_alerts"]:
        print(f"\nNew Alerts:")
        for alert in status["new_alerts"]:
            print(f"  [{alert['severity'].upper()}] {alert['message']}")

    # Record some results for success tracking
    for success in [True, True, True, False, True]:
        dashboard.record_task_result(success)

    # Get summary
    summary = dashboard.get_dashboard_summary()
    print(f"\nDashboard Summary: {json.dumps(summary, indent=2)}")
