"""Auditing and structured logging."""

import json
import logging
import threading
import time
from collections import deque
from typing import Dict, Any, Optional


class AuditorEstructurado:
    """Auditoría estructurada JSON sin PII (Personally Identifiable Information)."""
    
    def __init__(self, nombre_agente: str, max_registros: int = 10000):
        """
        Inicializa auditor estructurado.
        
        Args:
            nombre_agente: Nombre del agente
            max_registros: Máximo de registros en memoria antes de rotar
        """
        self.nombre_agente = nombre_agente
        self.max_registros = max_registros
        self.registros: deque = deque(maxlen=max_registros)
        self.lock = threading.Lock()
        self.contador_archivo = 0
    
    def registrar(self, 
                 evento: str,
                 nivel: str = "INFO",
                 detalles: Optional[Dict[str, Any]] = None,
                 ocultar_campos: Optional[list] = None):
        """
        Registra evento estructurado sin PII.
        
        Args:
            evento: Tipo de evento (TAREA_ENVIADA, TAREA_COMPLETADA, ERROR, etc)
            nivel: Nivel (INFO, WARNING, ERROR, CRITICAL)
            detalles: Dict con detalles del evento
            ocultar_campos: Campos a ofuscar (ej: ['password', 'token'])
        """
        detalles = detalles or {}
        ocultar_campos = ocultar_campos or []
        
        # Ofuscar campos sensibles
        detalles_seguros = {}
        for k, v in detalles.items():
            if k.lower() in ocultar_campos or any(pii in k.lower() for pii in ['password', 'token', 'secret', 'key']):
                detalles_seguros[k] = "***OCULTO***"
            else:
                detalles_seguros[k] = v
        
        registro = {
            "timestamp": time.time(),
            "timestamp_iso": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            "agente": self.nombre_agente,
            "evento": evento,
            "nivel": nivel,
            "detalles": detalles_seguros,
            "version_audit": "1.0"
        }
        
        with self.lock:
            self.registros.append(registro)
            
            # Log a través de logging también
            logging.log(
                getattr(logging, nivel, logging.INFO),
                f"[AUDIT] {evento}: {json.dumps(detalles_seguros)}"
            )
    
    def registrar_tarea_enviada(self, m_id: str, prioridad: int, ttl: int):
        """Registra envío de tarea."""
        self.registrar(
            "TAREA_ENVIADA",
            "INFO",
            {"tarea_id": m_id, "prioridad": prioridad, "ttl": ttl}
        )
    
    def registrar_tarea_completada(self, m_id: str, latencia: float):
        """Registra tarea completada."""
        self.registrar(
            "TAREA_COMPLETADA",
            "INFO",
            {"tarea_id": m_id, "latencia_segundos": round(latencia, 3)}
        )
    
    def registrar_error(self, m_id: str, tipo_error: str, mensaje: str):
        """Registra error en tarea."""
        self.registrar(
            "ERROR_TAREA",
            "ERROR",
            {"tarea_id": m_id, "tipo_error": tipo_error, "mensaje": mensaje[:100]}  # Limitar mensaje
        )
    
    def registrar_rate_limit(self, origen: str):
        """Registra cuando rate limit rechaza solicitud."""
        self.registrar(
            "RATE_LIMIT_EXCEDIDO",
            "WARNING",
            {"origen": origen}
        )
    
    def registrar_cambio_config(self, config_name: str, valor_anterior: str, valor_nuevo: str):
        """Registra cambios de configuración."""
        self.registrar(
            "CAMBIO_CONFIG",
            "INFO",
            {
                "config": config_name,
                "valor_anterior": valor_anterior,
                "valor_nuevo": valor_nuevo
            }
        )
    
    def obtener_registros(self, últimos: int = 100) -> list:
        """Retorna últimos N registros."""
        with self.lock:
            return list(self.registros)[-últimos:]
    
    def obtener_registros_por_evento(self, evento: str) -> list:
        """Retorna registros filtrados por evento."""
        with self.lock:
            return [r for r in self.registros if r["evento"] == evento]
    
    def exportar_json(self) -> str:
        """Exporta todos los registros como JSON."""
        with self.lock:
            return json.dumps(list(self.registros), default=str, indent=2)
    
    def limpiar(self):
        """Limpia todos los registros."""
        with self.lock:
            self.registros.clear()


from enum import Enum


class PoliticaError(Enum):
    """Políticas para manejar errores en tareas fallidas."""
    RETRY = "retry"  # Reintentar según retry_strategy
    DISCARD = "discard"  # Descartar silenciosamente
    DLQ = "dlq"  # Enviar a Dead Letter Queue
    BACKOFF = "backoff"  # Backoff exponencial antes de reintentar


class GestorPoliticasError:
    """Gestiona políticas de error para tareas fallidas."""
    
    def __init__(self, politica_defecto: PoliticaError = PoliticaError.DLQ):
        """
        Inicializa gestor de políticas de error.
        
        Args:
            politica_defecto: Política a usar por defecto
        """
        self.politica_defecto = politica_defecto
        self.politicas_por_tipo_error: Dict[str, PoliticaError] = {}
        self.lock = threading.Lock()
    
    def registrar_politica_por_tipo(self, tipo_error: str, politica: 'GestorPoliticasError.PoliticaError'):
        """Registra política específica para tipo de error."""
        with self.lock:
            self.politicas_por_tipo_error[tipo_error.lower()] = politica
            logging.info(f"Política de error registrada: {tipo_error} -> {politica.value}")
    
    def obtener_politica(self, tipo_error: str) -> 'GestorPoliticasError.PoliticaError':
        """Obtiene política para tipo de error."""
        with self.lock:
            tipo_key = tipo_error.lower()
            return self.politicas_por_tipo_error.get(tipo_key, self.politica_defecto)
    
    def debe_reintentar(self, tipo_error: str) -> bool:
        """Determina si se debe reintentar según política."""
        politica = self.obtener_politica(tipo_error)
        return politica in [PoliticaError.RETRY, PoliticaError.BACKOFF]
    
    def debe_enviar_dlq(self, tipo_error: str) -> bool:
        """Determina si enviar a DLQ según política."""
        politica = self.obtener_politica(tipo_error)
        return politica == PoliticaError.DLQ
