"""Alert management for container metrics."""

from __future__ import annotations

import fnmatch
import logging
from datetime import datetime
from typing import TYPE_CHECKING

from gaol.metrics.exceptions import AlertRuleNotFoundError
from gaol.metrics.models import AlertRule, AlertState, ContainerMetrics, MetricsSnapshot
from gaol.metrics.store import get_metrics_store

if TYPE_CHECKING:
    from gaol.metrics.store import MetricsStore

logger = logging.getLogger(__name__)


class AlertManager:
    """Manages alert rules and evaluates them against container metrics.

    Tracks alert state (firing, pending) and provides methods to add,
    remove, and check alert rules.

    Example:
        >>> manager = AlertManager()
        >>> manager.add_rule(AlertRule(
        ...     name="high-cpu",
        ...     metric=MetricType.CPU,
        ...     threshold=90.0,
        ...     comparison="gt",
        ...     severity=AlertSeverity.WARNING,
        ... ))
        >>> alerts = manager.evaluate(snapshot)
        >>> for alert in alerts:
        ...     print(alert.message)
    """

    def __init__(self, store: MetricsStore | None = None) -> None:
        """Initialize the alert manager.

        Args:
            store: Metrics store for rule persistence (uses global if None)
        """
        self._store = store or get_metrics_store()
        # Track alert state: key is (rule_name, container_name)
        self._alert_states: dict[tuple[str, str], AlertState] = {}

    def add_rule(self, rule: AlertRule) -> None:
        """Add a new alert rule.

        Args:
            rule: The alert rule to add

        Raises:
            AlertRuleExistsError: If rule with same name exists
        """
        self._store.add_rule(rule)
        logger.info(f"Added alert rule: {rule.name}")

    def delete_rule(self, name: str) -> bool:
        """Delete an alert rule by name.

        Args:
            name: Name of the rule to delete

        Returns:
            True if rule was deleted
        """
        result = self._store.delete_rule(name)
        if result:
            # Clear any alert states for this rule
            to_remove = [
                key for key in self._alert_states if key[0] == name
            ]
            for key in to_remove:
                del self._alert_states[key]
            logger.info(f"Deleted alert rule: {name}")
        return result

    def get_rule(self, name: str) -> AlertRule:
        """Get an alert rule by name.

        Args:
            name: Rule name

        Returns:
            The alert rule

        Raises:
            AlertRuleNotFoundError: If rule doesn't exist
        """
        rule = self._store.get_rule(name)
        if rule is None:
            raise AlertRuleNotFoundError(name)
        return rule

    def list_rules(self) -> list[AlertRule]:
        """List all alert rules."""
        return self._store.get_rules()

    def enable_rule(self, name: str) -> bool:
        """Enable an alert rule."""
        return self._store.enable_rule(name)

    def disable_rule(self, name: str) -> bool:
        """Disable an alert rule."""
        return self._store.disable_rule(name)

    def evaluate(self, snapshot: MetricsSnapshot) -> list[AlertState]:
        """Evaluate all enabled rules against a metrics snapshot.

        Args:
            snapshot: Current metrics snapshot

        Returns:
            List of alert states that are currently firing
        """
        rules = self._store.get_enabled_rules()
        firing_alerts: list[AlertState] = []
        now = datetime.now()

        for rule in rules:
            for container in snapshot.containers:
                # Check if container matches the pattern
                if not self._matches_pattern(container.container_name, rule.container_pattern):
                    continue

                # Get the metric value
                value = container.get_metric_value(rule.metric)

                # Evaluate the rule
                is_triggered = rule.evaluate(value)

                # Get or create alert state
                key = (rule.name, container.container_name)
                state = self._alert_states.get(key)

                if state is None:
                    state = AlertState(
                        rule_name=rule.name,
                        container_name=container.container_name,
                    )
                    self._alert_states[key] = state

                # Update state
                state.last_checked = now
                state.last_value = value

                if is_triggered:
                    if state.first_triggered is None:
                        # Just started triggering
                        state.first_triggered = now
                        logger.debug(
                            f"Alert {rule.name} triggered for {container.container_name} "
                            f"(value: {value})"
                        )

                    # Check duration requirement
                    duration = (now - state.first_triggered).total_seconds()
                    if duration >= rule.duration_seconds:
                        if not state.firing:
                            # Alert is now firing
                            state.firing = True
                            state.message = rule.format_message(
                                container.container_name, value
                            )
                            logger.warning(state.message)
                        firing_alerts.append(state)
                else:
                    if state.firing:
                        # Alert has resolved
                        logger.info(
                            f"Alert {rule.name} resolved for {container.container_name}"
                        )
                    state.firing = False
                    state.first_triggered = None
                    state.message = ""

        return firing_alerts

    def _matches_pattern(self, name: str, pattern: str) -> bool:
        """Check if a container name matches a glob pattern.

        Args:
            name: Container name
            pattern: Glob pattern (e.g., "*", "web-*", "db-?")

        Returns:
            True if name matches pattern
        """
        return fnmatch.fnmatch(name, pattern)

    def get_firing_alerts(self) -> list[AlertState]:
        """Get all currently firing alerts.

        Returns:
            List of alert states that are currently firing
        """
        return [
            state for state in self._alert_states.values()
            if state.firing
        ]

    def get_all_states(self) -> list[AlertState]:
        """Get all alert states (firing and non-firing).

        Returns:
            List of all tracked alert states
        """
        return list(self._alert_states.values())

    def clear_states(self) -> None:
        """Clear all tracked alert states.

        Useful when restarting monitoring or for testing.
        """
        self._alert_states.clear()

    def check_container(
        self, container: ContainerMetrics
    ) -> list[AlertState]:
        """Check a single container against all enabled rules.

        Args:
            container: Container metrics to check

        Returns:
            List of firing alerts for this container
        """
        # Create a temporary snapshot with just this container
        snapshot = MetricsSnapshot(containers=[container])
        all_firing = self.evaluate(snapshot)
        # Filter to just this container
        return [
            alert for alert in all_firing
            if alert.container_name == container.container_name
        ]


# Global alert manager instance
_manager: AlertManager | None = None


def get_alert_manager() -> AlertManager:
    """Get the global alert manager instance."""
    global _manager
    if _manager is None:
        _manager = AlertManager()
    return _manager
