"""
Type definitions for Self-Healing System

Phase 4 of SNBG Intelligence Upgrade Plan.
Provides circuit breaker, anomaly detection, auto-scaling, and chaos engineering.
"""

from datetime import datetime, timezone
from enum import Enum
from typing import Any

from pydantic import BaseModel, Field


# ============================================================================
# Circuit Breaker Enums
# ============================================================================


class CircuitState(str, Enum):
    """Circuit breaker states"""

    CLOSED = "closed"  # Normal operation, requests flow through
    OPEN = "open"  # Circuit tripped, requests blocked
    HALF_OPEN = "half_open"  # Testing if service recovered


class TripReason(str, Enum):
    """Reasons for circuit breaker trip"""

    ERROR_RATE = "error_rate"  # Too many errors
    TIMEOUT_RATE = "timeout_rate"  # Too many timeouts
    LATENCY_SPIKE = "latency_spike"  # Response time too high
    RESOURCE_EXHAUSTION = "resource_exhaustion"  # Memory/CPU/connections
    MANUAL = "manual"  # Manually tripped
    DEPENDENCY_FAILURE = "dependency_failure"  # Upstream service failed


# ============================================================================
# Anomaly Detection Enums
# ============================================================================


class AnomalyType(str, Enum):
    """Types of anomalies detected"""

    SPIKE = "spike"  # Sudden increase
    DROP = "drop"  # Sudden decrease
    TREND_CHANGE = "trend_change"  # Direction change
    SEASONALITY_VIOLATION = "seasonality_violation"  # Unexpected pattern
    OUTLIER = "outlier"  # Statistical outlier
    MISSING_DATA = "missing_data"  # Data gaps
    SCHEMA_ANOMALY = "schema_anomaly"  # Unexpected schema
    CORRELATION_BREAK = "correlation_break"  # Expected correlation broken


class AnomalySeverity(str, Enum):
    """Severity of anomalies"""

    LOW = "low"  # Informational
    MEDIUM = "medium"  # Needs attention
    HIGH = "high"  # Immediate action needed
    CRITICAL = "critical"  # System at risk


class DetectionMethod(str, Enum):
    """Anomaly detection methods"""

    ZSCORE = "zscore"  # Statistical z-score
    IQR = "iqr"  # Interquartile range
    MAD = "mad"  # Median absolute deviation
    PROPHET = "prophet"  # Facebook Prophet
    ISOLATION_FOREST = "isolation_forest"  # ML isolation forest
    EWMA = "ewma"  # Exponentially weighted moving average
    THRESHOLD = "threshold"  # Simple threshold


# ============================================================================
# Healing Action Enums
# ============================================================================


class HealingActionType(str, Enum):
    """Types of self-healing actions"""

    RESTART_SERVICE = "restart_service"
    SCALE_UP = "scale_up"
    SCALE_DOWN = "scale_down"
    FAILOVER = "failover"
    CACHE_CLEAR = "cache_clear"
    RATE_LIMIT = "rate_limit"
    CIRCUIT_OPEN = "circuit_open"
    CIRCUIT_CLOSE = "circuit_close"
    RETRY_BACKLOG = "retry_backlog"
    ALERT_ONCALL = "alert_oncall"
    ROLLBACK = "rollback"
    QUARANTINE = "quarantine"


class HealingStatus(str, Enum):
    """Status of healing actions"""

    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    SKIPPED = "skipped"
    ROLLED_BACK = "rolled_back"


class HealingTrigger(str, Enum):
    """What triggered the healing action"""

    ANOMALY = "anomaly"
    CIRCUIT_BREAKER = "circuit_breaker"
    HEALTH_CHECK = "health_check"
    SCHEDULED = "scheduled"
    MANUAL = "manual"
    CHAOS_TEST = "chaos_test"


# ============================================================================
# Circuit Breaker Models
# ============================================================================


class CircuitBreakerConfig(BaseModel):
    """Configuration for a circuit breaker"""

    name: str
    error_threshold: float = 0.5  # 50% error rate trips
    timeout_threshold: float = 0.3  # 30% timeout rate trips
    latency_threshold_ms: float = 5000.0  # 5s latency trips
    window_size_seconds: int = 60  # Evaluation window
    min_requests: int = 10  # Min requests before evaluation
    recovery_timeout_seconds: int = 30  # Time in OPEN before HALF_OPEN
    half_open_max_requests: int = 3  # Test requests in HALF_OPEN
    cooldown_seconds: int = 60  # Cooldown after recovery


class CircuitBreakerStats(BaseModel):
    """Statistics for circuit breaker"""

    total_requests: int = 0
    successful_requests: int = 0
    failed_requests: int = 0
    timeout_requests: int = 0
    avg_latency_ms: float = 0.0
    p99_latency_ms: float = 0.0
    last_failure_time: datetime | None = None
    last_success_time: datetime | None = None
    consecutive_failures: int = 0
    consecutive_successes: int = 0


class CircuitBreakerState(BaseModel):
    """Current state of a circuit breaker"""

    name: str
    state: CircuitState = CircuitState.CLOSED
    trip_reason: TripReason | None = None
    tripped_at: datetime | None = None
    stats: CircuitBreakerStats = Field(default_factory=CircuitBreakerStats)
    config: CircuitBreakerConfig
    next_retry_at: datetime | None = None


class CircuitBreakerEvent(BaseModel):
    """Event from circuit breaker state change"""

    circuit_name: str
    event_type: str  # state_change, request, trip, reset
    old_state: CircuitState | None = None
    new_state: CircuitState | None = None
    reason: str | None = None
    timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    metadata: dict[str, Any] = Field(default_factory=dict)


# ============================================================================
# Anomaly Detection Models
# ============================================================================


class MetricPoint(BaseModel):
    """A single metric data point"""

    timestamp: datetime
    value: float
    metric_name: str
    labels: dict[str, str] = Field(default_factory=dict)


class AnomalyDetectionConfig(BaseModel):
    """Configuration for anomaly detection"""

    metric_name: str
    detection_method: DetectionMethod = DetectionMethod.ZSCORE
    sensitivity: float = 3.0  # Z-score threshold
    window_size: int = 100  # Data points for baseline
    min_data_points: int = 30  # Minimum for detection
    seasonality_period: int | None = None  # e.g., 24 for hourly daily pattern
    correlation_metrics: list[str] = Field(default_factory=list)


class Anomaly(BaseModel):
    """A detected anomaly"""

    anomaly_id: str
    anomaly_type: AnomalyType
    severity: AnomalySeverity
    metric_name: str
    detected_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    detection_method: DetectionMethod
    expected_value: float
    actual_value: float
    deviation: float  # How far from expected (z-score or %)
    confidence: float = 0.0  # 0-1 confidence in detection
    context: dict[str, Any] = Field(default_factory=dict)
    labels: dict[str, str] = Field(default_factory=dict)


class AnomalyDetectionResult(BaseModel):
    """Result of anomaly detection run"""

    metric_name: str
    detection_time_ms: float = 0.0
    data_points_analyzed: int = 0
    anomalies: list[Anomaly] = Field(default_factory=list)
    baseline_mean: float = 0.0
    baseline_std: float = 0.0
    is_healthy: bool = True
    next_check_at: datetime | None = None


# ============================================================================
# Health Check Models
# ============================================================================


class HealthCheckType(str, Enum):
    """Types of health checks"""

    LIVENESS = "liveness"  # Is service alive?
    READINESS = "readiness"  # Is service ready for traffic?
    DEPENDENCY = "dependency"  # Are dependencies healthy?
    DEEP = "deep"  # Full system check


class HealthStatus(str, Enum):
    """Health check status"""

    HEALTHY = "healthy"
    DEGRADED = "degraded"
    UNHEALTHY = "unhealthy"
    UNKNOWN = "unknown"


class ComponentHealth(BaseModel):
    """Health of a single component"""

    component_name: str
    status: HealthStatus
    check_type: HealthCheckType
    latency_ms: float = 0.0
    message: str | None = None
    last_check_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    consecutive_failures: int = 0
    metadata: dict[str, Any] = Field(default_factory=dict)


class SystemHealth(BaseModel):
    """Overall system health"""

    status: HealthStatus
    components: list[ComponentHealth] = Field(default_factory=list)
    healthy_count: int = 0
    degraded_count: int = 0
    unhealthy_count: int = 0
    check_duration_ms: float = 0.0
    checked_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


# ============================================================================
# Healing Action Models
# ============================================================================


class HealingAction(BaseModel):
    """A self-healing action to execute"""

    action_id: str
    action_type: HealingActionType
    target: str  # Service, resource, or component name
    trigger: HealingTrigger
    parameters: dict[str, Any] = Field(default_factory=dict)
    priority: int = 1  # 1=highest
    max_retries: int = 3
    timeout_seconds: int = 300
    rollback_action: "HealingAction | None" = None
    requires_approval: bool = False
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


class HealingResult(BaseModel):
    """Result of executing a healing action"""

    action_id: str
    action_type: HealingActionType
    status: HealingStatus
    target: str
    started_at: datetime | None = None
    completed_at: datetime | None = None
    duration_ms: float = 0.0
    retries_used: int = 0
    error_message: str | None = None
    metrics_before: dict[str, float] = Field(default_factory=dict)
    metrics_after: dict[str, float] = Field(default_factory=dict)
    improvement: float = 0.0  # Percentage improvement


class HealingPlan(BaseModel):
    """Plan for healing multiple issues"""

    plan_id: str
    trigger: HealingTrigger
    actions: list[HealingAction] = Field(default_factory=list)
    total_actions: int = 0
    estimated_duration_seconds: int = 0
    risk_level: str = "low"
    requires_approval: bool = False
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


class HealingExecutionResult(BaseModel):
    """Result of executing a healing plan"""

    plan_id: str
    status: HealingStatus
    actions_completed: int = 0
    actions_failed: int = 0
    actions_total: int = 0
    results: list[HealingResult] = Field(default_factory=list)
    started_at: datetime | None = None
    completed_at: datetime | None = None
    total_duration_ms: float = 0.0
    overall_improvement: float = 0.0


# ============================================================================
# Chaos Engineering Models
# ============================================================================


class ChaosExperimentType(str, Enum):
    """Types of chaos experiments"""

    LATENCY_INJECTION = "latency_injection"
    ERROR_INJECTION = "error_injection"
    RESOURCE_STRESS = "resource_stress"
    NETWORK_PARTITION = "network_partition"
    SERVICE_KILL = "service_kill"
    DEPENDENCY_FAILURE = "dependency_failure"
    DATA_CORRUPTION = "data_corruption"


class ChaosExperiment(BaseModel):
    """A chaos engineering experiment"""

    experiment_id: str
    experiment_type: ChaosExperimentType
    target: str
    parameters: dict[str, Any] = Field(default_factory=dict)
    duration_seconds: int = 60
    blast_radius: str = "single"  # single, service, zone, region
    safety_checks: list[str] = Field(default_factory=list)
    rollback_trigger: str | None = None  # Condition to auto-rollback
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


class ChaosExperimentResult(BaseModel):
    """Result of chaos experiment"""

    experiment_id: str
    experiment_type: ChaosExperimentType
    target: str
    success: bool  # Did system heal correctly?
    healing_triggered: bool = False
    healing_time_ms: float = 0.0  # Time to recover
    started_at: datetime | None = None
    ended_at: datetime | None = None
    observations: list[str] = Field(default_factory=list)
    metrics_impact: dict[str, float] = Field(default_factory=dict)
    recommendations: list[str] = Field(default_factory=list)


# ============================================================================
# Agent State and Configuration
# ============================================================================


class SelfHealingState(BaseModel):
    """State of the self-healing system"""

    agent_id: str
    status: str = "idle"  # idle, monitoring, healing
    circuits_monitored: int = 0
    metrics_monitored: int = 0
    anomalies_detected: int = 0
    healing_actions_executed: int = 0
    successful_healings: int = 0
    last_anomaly_at: datetime | None = None
    last_healing_at: datetime | None = None
    uptime_seconds: float = 0.0


class SelfHealingConfig(BaseModel):
    """Configuration for Self-Healing System"""

    # Redis connection
    redis_url: str = "redis://localhost:6379"

    # Circuit breaker defaults
    default_error_threshold: float = 0.5
    default_timeout_threshold: float = 0.3
    default_recovery_timeout_seconds: int = 30

    # Anomaly detection defaults
    default_detection_method: DetectionMethod = DetectionMethod.ZSCORE
    default_sensitivity: float = 3.0
    anomaly_check_interval_seconds: int = 60

    # Health check settings
    health_check_interval_seconds: int = 30
    health_check_timeout_seconds: int = 10
    consecutive_failures_threshold: int = 3

    # Healing settings
    auto_heal_enabled: bool = True
    require_approval_for_critical: bool = True
    max_concurrent_healings: int = 3
    healing_cooldown_seconds: int = 300

    # Chaos engineering settings
    chaos_enabled: bool = False  # Disabled by default for safety
    chaos_max_duration_seconds: int = 300
    chaos_safety_timeout_seconds: int = 60

    # Learning settings
    learning_enabled: bool = True
    min_observations_for_baseline: int = 100
