"""
alert_manager.py — Alert severity escalation for emergency behavior (Issue #169).

Alert levels
────────────
  NONE     : no action
  MINOR    : advisory beep  → publish to /saltybot/alert_beep
  MAJOR    : stop + LED flash → publish to /saltybot/alert_flash; cmd_vel override
  CRITICAL : full shutdown + MQTT → publish to /saltybot/e_stop + /saltybot/critical_alert

Escalation
──────────
  If major_count_threshold MAJOR alerts occur within escalation_window_s, the
  next MAJOR is promoted to CRITICAL.  This catches persistent stuck / repeated
  collision scenarios.

Suppression
───────────
  Identical (type, level) alerts are suppressed within suppression_s to avoid
  flooding downstream topics.

Pure module — no ROS2 dependency.
"""

from __future__ import annotations

from collections import deque
from dataclasses import dataclass
from enum import Enum
from typing import Optional

from saltybot_emergency.threat_detector import ThreatEvent, ThreatLevel, ThreatType


# ── Alert level ───────────────────────────────────────────────────────────────

class AlertLevel(Enum):
    NONE     = 0
    MINOR    = 1   # beep
    MAJOR    = 2   # stop + flash
    CRITICAL = 3   # shutdown + MQTT


# ── Alert ─────────────────────────────────────────────────────────────────────

@dataclass
class Alert:
    level:       AlertLevel
    source:      str     # ThreatType value string
    message:     str
    timestamp_s: float


# ── AlertManager ─────────────────────────────────────────────────────────────

class AlertManager:
    """
    Converts ThreatEvents to Alerts with escalation and suppression logic.

    Parameters
    ----------
    major_count_threshold : number of MAJOR alerts within window to escalate
    escalation_window_s   : sliding window for escalation counting (s)
    suppression_s         : suppress duplicate (type, level) alerts within this period
    """

    def __init__(
        self,
        major_count_threshold: int   = 3,
        escalation_window_s:   float = 10.0,
        suppression_s:         float = 1.0,
    ):
        self._major_threshold  = max(1, int(major_count_threshold))
        self._esc_window       = float(escalation_window_s)
        self._suppress         = float(suppression_s)
        self._major_times:     deque  = deque()            # timestamps of MAJOR alerts
        self._last_seen:       dict   = {}                 # (type, level) → timestamp

    # ── Update ────────────────────────────────────────────────────────────────

    def update(self, threat: ThreatEvent) -> Optional[Alert]:
        """
        Convert one ThreatEvent to an Alert, applying escalation and suppression.

        Returns None if threat is CLEAR or the alert is suppressed.
        """
        if threat.level == ThreatLevel.CLEAR:
            return None

        now = threat.timestamp_s
        alert_level = _threat_to_alert(threat.level)

        # ── Suppression ───────────────────────────────────────────────────────
        key = (threat.threat_type, threat.level)
        last = self._last_seen.get(key)
        if last is not None and (now - last) < self._suppress:
            return None
        self._last_seen[key] = now

        # ── Escalation ────────────────────────────────────────────────────────
        if alert_level == AlertLevel.MAJOR:
            # Prune old timestamps
            while self._major_times and (now - self._major_times[0]) > self._esc_window:
                self._major_times.popleft()
            self._major_times.append(now)
            if len(self._major_times) >= self._major_threshold:
                alert_level = AlertLevel.CRITICAL

        msg = _build_message(alert_level, threat)
        return Alert(
            level=alert_level,
            source=threat.threat_type.value,
            message=msg,
            timestamp_s=now,
        )

    def reset(self) -> None:
        """Clear escalation history and suppression state."""
        self._major_times.clear()
        self._last_seen.clear()


# ── Helpers ───────────────────────────────────────────────────────────────────

def _threat_to_alert(level: ThreatLevel) -> AlertLevel:
    return {
        ThreatLevel.MINOR:    AlertLevel.MINOR,
        ThreatLevel.MAJOR:    AlertLevel.MAJOR,
        ThreatLevel.CRITICAL: AlertLevel.CRITICAL,
    }.get(level, AlertLevel.NONE)


def _build_message(level: AlertLevel, threat: ThreatEvent) -> str:
    prefix = {
        AlertLevel.MINOR:    "[MINOR]",
        AlertLevel.MAJOR:    "[MAJOR]",
        AlertLevel.CRITICAL: "[CRITICAL]",
    }.get(level, "[?]")
    return f"{prefix} {threat.threat_type.value}: {threat.detail}"
