"""
Monitor de saúde RabbitMQ.

Este módulo implementa monitoramento de saúde das conexões
e do servidor RabbitMQ.
"""

import asyncio
import time
from collections.abc import Callable
from dataclasses import dataclass
from enum import Enum

from pika.exceptions import AMQPChannelError, AMQPConnectionError

from rabbitmq.connection_manager import connection_manager
from shared.models.connection import ConnectionStatus
from shared.utils.logging import get_logger


class HealthStatus(str, Enum):
    """Status de saúde."""
    HEALTHY = "healthy"
    WARNING = "warning"
    CRITICAL = "critical"
    UNKNOWN = "unknown"


@dataclass
class HealthCheck:
    """Resultado de verificação de saúde."""
    name: str
    status: HealthStatus
    message: str
    details: dict | None = None
    timestamp: float = None

    def __post_init__(self):
        if self.timestamp is None:
            self.timestamp = time.time()


@dataclass
class HealthMetrics:
    """Métricas de saúde."""
    connection_count: int
    active_connections: int
    failed_connections: int
    avg_response_time: float
    memory_usage: float | None = None
    disk_usage: float | None = None
    cpu_usage: float | None = None


class HealthMonitor:
    """Monitor de saúde RabbitMQ."""

    def __init__(self):
        """Inicializa o monitor de saúde."""
        self.logger = get_logger(__name__)
        self.health_checks: list[Callable] = []
        self.metrics_history: list[HealthMetrics] = []
        self.max_history_size = 100
        self._register_default_checks()

    def _register_default_checks(self):
        """Registra verificações de saúde padrão."""
        self.health_checks.extend([
            self._check_connection_health,
            self._check_server_connectivity,
            self._check_channel_health,
            self._check_memory_usage,
        ])

    async def check_connection_health(self, connection_id: str) -> HealthCheck:
        """
        Verifica a saúde de uma conexão específica.

        Args:
            connection_id: ID da conexão

        Returns:
            Resultado da verificação de saúde
        """
        try:
            # Verificar se conexão existe
            if connection_id not in connection_manager.connections:
                return HealthCheck(
                    name="connection_exists",
                    status=HealthStatus.CRITICAL,
                    message=f"Conexão {connection_id} não encontrada"
                )

            connection = connection_manager.connections[connection_id]

            # Verificar status da conexão
            if connection.status != ConnectionStatus.CONNECTED:
                return HealthCheck(
                    name="connection_status",
                    status=HealthStatus.CRITICAL,
                    message=f"Conexão {connection_id} não está conectada",
                    details={"status": connection.status.value}
                )

            # Verificar se conexão ainda está ativa
            if connection_id in connection_manager.connection_objects:
                conn_obj = connection_manager.connection_objects[connection_id]
                if conn_obj.is_closed:
                    return HealthCheck(
                        name="connection_active",
                        status=HealthStatus.CRITICAL,
                        message=f"Conexão {connection_id} está fechada"
                    )

            # Verificar canal
            channel = await connection_manager.get_channel(connection_id)
            if not channel:
                return HealthCheck(
                    name="channel_health",
                    status=HealthStatus.CRITICAL,
                    message=f"Canal da conexão {connection_id} não está disponível"
                )

            if channel.is_closed:
                return HealthCheck(
                    name="channel_health",
                    status=HealthStatus.CRITICAL,
                    message=f"Canal da conexão {connection_id} está fechado"
                )

            # Teste de conectividade básico
            try:
                # Tentar declarar uma fila temporária
                temp_queue = f"health_check_{int(time.time())}"
                method = channel.queue_declare(queue=temp_queue, auto_delete=True)
                channel.queue_delete(queue=temp_queue)

                return HealthCheck(
                    name="connection_health",
                    status=HealthStatus.HEALTHY,
                    message=f"Conexão {connection_id} está saudável",
                    details={
                        "uptime": time.time() - connection.created_at,
                        "message_count": method.method.message_count,
                        "consumer_count": method.method.consumer_count,
                    }
                )

            except Exception as e:
                return HealthCheck(
                    name="connection_health",
                    status=HealthStatus.CRITICAL,
                    message=f"Falha no teste de conectividade: {str(e)}"
                )

        except Exception as e:
            return HealthCheck(
                name="connection_health",
                status=HealthStatus.UNKNOWN,
                message=f"Erro ao verificar saúde da conexão: {str(e)}"
            )

    async def check_server_health(self, connection_id: str) -> HealthCheck:
        """
        Verifica a saúde do servidor RabbitMQ.

        Args:
            connection_id: ID da conexão para usar no teste

        Returns:
            Resultado da verificação de saúde
        """
        try:
            channel = await connection_manager.get_channel(connection_id)
            if not channel:
                return HealthCheck(
                    name="server_health",
                    status=HealthStatus.CRITICAL,
                    message="Não é possível obter canal para verificação do servidor"
                )

            # Medir tempo de resposta
            start_time = time.time()

            # Teste básico de conectividade
            method = channel.queue_declare(queue="", exclusive=True)
            queue_name = method.method.queue
            channel.queue_delete(queue=queue_name)

            response_time = (time.time() - start_time) * 1000  # em ms

            # Determinar status baseado no tempo de resposta
            if response_time < 100:
                status = HealthStatus.HEALTHY
                message = "Servidor RabbitMQ está respondendo rapidamente"
            elif response_time < 500:
                status = HealthStatus.WARNING
                message = "Servidor RabbitMQ está respondendo lentamente"
            else:
                status = HealthStatus.CRITICAL
                message = "Servidor RabbitMQ está respondendo muito lentamente"

            return HealthCheck(
                name="server_health",
                status=status,
                message=message,
                details={
                    "response_time_ms": response_time,
                    "queue_name": queue_name,
                }
            )

        except AMQPConnectionError as e:
            return HealthCheck(
                name="server_health",
                status=HealthStatus.CRITICAL,
                message=f"Erro de conexão com servidor: {str(e)}"
            )
        except AMQPChannelError as e:
            return HealthCheck(
                name="server_health",
                status=HealthStatus.CRITICAL,
                message=f"Erro de canal: {str(e)}"
            )
        except Exception as e:
            return HealthCheck(
                name="server_health",
                status=HealthStatus.UNKNOWN,
                message=f"Erro inesperado: {str(e)}"
            )

    async def check_cluster_health(self, connection_id: str) -> HealthCheck:
        """
        Verifica a saúde do cluster RabbitMQ.

        Args:
            connection_id: ID da conexão

        Returns:
            Resultado da verificação de saúde
        """
        try:
            # Esta é uma implementação simplificada
            # Em produção, seria necessário usar a API de gerenciamento HTTP

            channel = await connection_manager.get_channel(connection_id)
            if not channel:
                return HealthCheck(
                    name="cluster_health",
                    status=HealthStatus.CRITICAL,
                    message="Não é possível obter canal para verificação do cluster"
                )

            # Verificação básica - assumir cluster saudável se conexão funciona
            return HealthCheck(
                name="cluster_health",
                status=HealthStatus.HEALTHY,
                message="Cluster RabbitMQ está funcionando",
                details={
                    "note": "Verificação simplificada - use API de gerenciamento para detalhes completos"
                }
            )

        except Exception as e:
            return HealthCheck(
                name="cluster_health",
                status=HealthStatus.UNKNOWN,
                message=f"Erro ao verificar cluster: {str(e)}"
            )

    async def get_health_metrics(self) -> HealthMetrics:
        """
        Obtém métricas de saúde atuais.

        Returns:
            Métricas de saúde
        """
        try:
            connections = await connection_manager.list_connections(include_stats=True)

            total_connections = len(connections)
            active_connections = len([c for c in connections.values() if c.status == ConnectionStatus.CONNECTED])
            failed_connections = total_connections - active_connections

            # Calcular tempo médio de resposta (simplificado)
            response_times = []
            for connection in connections.values():
                if connection.stats and "uptime" in connection.stats:
                    # Usar uptime como proxy para tempo de resposta
                    response_times.append(connection.stats["uptime"])

            avg_response_time = sum(response_times) / len(response_times) if response_times else 0

            metrics = HealthMetrics(
                connection_count=total_connections,
                active_connections=active_connections,
                failed_connections=failed_connections,
                avg_response_time=avg_response_time,
            )

            # Armazenar no histórico
            self.metrics_history.append(metrics)
            if len(self.metrics_history) > self.max_history_size:
                self.metrics_history.pop(0)

            return metrics

        except Exception as e:
            self.logger.error("Erro ao obter métricas de saúde", error=str(e))
            return HealthMetrics(
                connection_count=0,
                active_connections=0,
                failed_connections=0,
                avg_response_time=0,
            )

    async def run_health_checks(self, connection_id: str | None = None) -> list[HealthCheck]:
        """
        Executa todas as verificações de saúde.

        Args:
            connection_id: ID da conexão específica (opcional)

        Returns:
            Lista de resultados das verificações
        """
        results = []

        # Verificações específicas de conexão
        if connection_id:
            results.append(await self.check_connection_health(connection_id))
            results.append(await self.check_server_health(connection_id))
            results.append(await self.check_cluster_health(connection_id))
        else:
            # Verificações gerais
            connections = await connection_manager.list_connections()
            if connections:
                # Usar primeira conexão disponível
                first_connection_id = next(iter(connections.keys()))
                results.append(await self.check_server_health(first_connection_id))
                results.append(await self.check_cluster_health(first_connection_id))
            else:
                results.append(HealthCheck(
                    name="no_connections",
                    status=HealthStatus.CRITICAL,
                    message="Nenhuma conexão ativa encontrada"
                ))

        # Executar verificações registradas
        for check_func in self.health_checks:
            try:
                if asyncio.iscoroutinefunction(check_func):
                    result = await check_func(connection_id)
                else:
                    result = check_func(connection_id)

                if isinstance(result, HealthCheck):
                    results.append(result)
            except Exception as e:
                results.append(HealthCheck(
                    name=check_func.__name__,
                    status=HealthStatus.UNKNOWN,
                    message=f"Erro na verificação: {str(e)}"
                ))

        return results

    async def _check_connection_health(self, connection_id: str | None = None) -> HealthCheck:
        """Verificação de saúde das conexões."""
        connections = await connection_manager.list_connections()

        if not connections:
            return HealthCheck(
                name="connection_health",
                status=HealthStatus.CRITICAL,
                message="Nenhuma conexão ativa"
            )

        healthy_connections = len([c for c in connections.values() if c.status == ConnectionStatus.CONNECTED])
        total_connections = len(connections)

        if healthy_connections == total_connections:
            status = HealthStatus.HEALTHY
            message = f"Todas as {total_connections} conexões estão saudáveis"
        elif healthy_connections > 0:
            status = HealthStatus.WARNING
            message = f"{healthy_connections}/{total_connections} conexões estão saudáveis"
        else:
            status = HealthStatus.CRITICAL
            message = "Nenhuma conexão está saudável"

        return HealthCheck(
            name="connection_health",
            status=status,
            message=message,
            details={
                "total_connections": total_connections,
                "healthy_connections": healthy_connections,
                "unhealthy_connections": total_connections - healthy_connections,
            }
        )

    async def _check_server_connectivity(self, connection_id: str | None = None) -> HealthCheck:
        """Verificação de conectividade do servidor."""
        if connection_id:
            return await self.check_server_health(connection_id)
        else:
            return HealthCheck(
                name="server_connectivity",
                status=HealthStatus.UNKNOWN,
                message="ID da conexão não fornecido para verificação de conectividade"
            )

    async def _check_channel_health(self, connection_id: str | None = None) -> HealthCheck:
        """Verificação de saúde dos canais."""
        if not connection_id:
            return HealthCheck(
                name="channel_health",
                status=HealthStatus.UNKNOWN,
                message="ID da conexão não fornecido para verificação de canal"
            )

        channel = await connection_manager.get_channel(connection_id)
        if not channel:
            return HealthCheck(
                name="channel_health",
                status=HealthStatus.CRITICAL,
                message=f"Canal não disponível para conexão {connection_id}"
            )

        if channel.is_closed:
            return HealthCheck(
                name="channel_health",
                status=HealthStatus.CRITICAL,
                message=f"Canal fechado para conexão {connection_id}"
            )

        return HealthCheck(
            name="channel_health",
            status=HealthStatus.HEALTHY,
            message=f"Canal saudável para conexão {connection_id}"
        )

    async def _check_memory_usage(self, connection_id: str | None = None) -> HealthCheck:
        """Verificação de uso de memória (simplificada)."""
        # Esta é uma implementação simplificada
        # Em produção, seria necessário usar a API de gerenciamento HTTP

        return HealthCheck(
            name="memory_usage",
            status=HealthStatus.HEALTHY,
            message="Verificação de memória não implementada",
            details={
                "note": "Use API de gerenciamento HTTP para métricas detalhadas"
            }
        )


# Instância global do monitor
health_monitor = HealthMonitor()
