"""Configuration classes for FEID-MAS components."""

import json
import time
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Dict, Any, Optional, List, Union

from .queue_policy import PoliticaCola
from ..audit import PoliticaError


@dataclass
class AgentProfile:
    """Optional agent identity metadata (role/skills/limits)."""

    display_name: Optional[str] = None
    role: Optional[str] = None
    skills: List[str] = field(default_factory=list)
    specialties: List[str] = field(default_factory=list)
    limitations: List[str] = field(default_factory=list)
    tags: List[str] = field(default_factory=list)

    def validar(self) -> bool:
        fields = [
            ("display_name", self.display_name),
            ("role", self.role),
        ]
        for name, value in fields:
            if value is not None and not isinstance(value, str):
                raise ValueError(f"{name} debe ser str o None")

        list_fields = [
            ("skills", self.skills),
            ("specialties", self.specialties),
            ("limitations", self.limitations),
            ("tags", self.tags),
        ]
        for name, values in list_fields:
            if not isinstance(values, list) or not all(isinstance(v, str) for v in values):
                raise ValueError(f"{name} debe ser lista de str")
        return True

    def a_diccionario(self) -> Dict[str, Any]:
        return {
            "display_name": self.display_name,
            "role": self.role,
            "skills": list(self.skills),
            "specialties": list(self.specialties),
            "limitations": list(self.limitations),
            "tags": list(self.tags),
        }

    @classmethod
    def desde_diccionario(cls, datos: Dict[str, Any]) -> "AgentProfile":
        return cls(
            display_name=datos.get("display_name"),
            role=datos.get("role"),
            skills=list(datos.get("skills", [])),
            specialties=list(datos.get("specialties", [])),
            limitations=list(datos.get("limitations", [])),
            tags=list(datos.get("tags", [])),
        )


class Config(ABC):
    """Base class for versioned configuration."""
    
    VERSION_ACTUAL = "1.0"
    
    def __init__(self):
        self.version = self.VERSION_ACTUAL
        self.timestamp_creacion = time.time()
    
    def actualizar(self, **kwargs) -> Dict[str, Any]:
        cambios = {}
        for clave, valor in kwargs.items():
            if hasattr(self, clave):
                valor_anterior = getattr(self, clave)
                setattr(self, clave, valor)
                cambios[clave] = {"anterior": valor_anterior, "nuevo": valor}
        return cambios
    
    @abstractmethod
    def validar(self) -> bool:
        pass
    
    def a_diccionario(self) -> Dict[str, Any]:
        return {
            k: v for k, v in self.__dict__.items()
            if not k.startswith('_')
        }
    
    def a_json(self) -> str:
        return json.dumps(self.a_diccionario(), default=str, indent=2)
    
    @classmethod
    def desde_diccionario(cls, datos: Dict[str, Any]) -> 'Config':
        params = {k: v for k, v in datos.items() 
                 if k not in ['version', 'timestamp_creacion']}
        return cls(**params)
    
    @classmethod
    def desde_json(cls, json_str: str) -> 'Config':
        datos = json.loads(json_str)
        return cls.desde_diccionario(datos)


class ConfigAgente(Config):
    """Agent configuration for AgenteMaestroNASA."""
    
    VERSION_ACTUAL = "0.1.0"
    
    def __init__(self, 
                 capacidad: int = 50,
                 max_reintentos: int = 3,
                 ttl_defecto: float = 10.0,
                 backpressure_threshold: float = 0.8,
                 max_workers: int = 4,
                 circuit_breaker_threshold: int = 5,
                 rate_limit_tokens: float = 100.0,
                 rate_limit_capacidad: float = 100.0,
                 payload_max_bytes: int = 1_000_000,
                 max_task_bytes: Optional[int] = None,
                 max_result_bytes: int = 1_000_000,
                 payload_overflow_policy: str = "truncate",
                 task_timeout_seconds: Optional[float] = None,
                 handler_timeout_seconds: Optional[float] = None,
                 renew_ttl_on_retry: bool = False,
                 metricas_ventana_segundos: int = 60,
                 politica_cola: PoliticaCola = PoliticaCola.PRIORITY,
                 politica_error_defecto: PoliticaError = PoliticaError.RETRY,
                 anti_inanicion_ratio_minimo: float = 0.2,
                 sandbox_habilitar_whitelist: bool = False,
                 sondeo_umbral_inactividad: float = 60.0,
                 max_memoria: int = 2000,
                 max_registros_por_archivo: int = 2000,
                 frecuencia_percepcion: float = 5.0,
                 # Conditional component flags (flavors)
                 enable_circuit_breaker: bool = True,
                 enable_rate_limiter: bool = True,
                 enable_auditor: bool = True,
                 enable_sandbox: bool = True,
                 enable_health_probes: bool = True,
                 enable_anti_starvation: bool = True,
                 ciclo_timeout_segundos: float = 0.1,
                 protocol_strict_mode: bool = False,
                 auto_start: bool = True,
                 agent_profile: Optional[Union[AgentProfile, Dict[str, Any]]] = None):
        super().__init__()
        self.version = self.VERSION_ACTUAL
        self.capacidad = capacidad
        self.max_reintentos = max_reintentos
        self.ttl_defecto = ttl_defecto
        self.backpressure_threshold = backpressure_threshold
        self.max_workers = max_workers
        self.circuit_breaker_threshold = circuit_breaker_threshold
        self.rate_limit_tokens = rate_limit_tokens
        self.rate_limit_capacidad = rate_limit_capacidad
        resolved_max_task_bytes = max_task_bytes if max_task_bytes is not None else payload_max_bytes
        self.payload_max_bytes = resolved_max_task_bytes
        self.max_task_bytes = resolved_max_task_bytes
        self.max_result_bytes = max_result_bytes
        self.payload_overflow_policy = payload_overflow_policy
        self.task_timeout_seconds = task_timeout_seconds
        self.handler_timeout_seconds = handler_timeout_seconds
        self.renew_ttl_on_retry = renew_ttl_on_retry
        self.metricas_ventana_segundos = metricas_ventana_segundos
        self.politica_cola = politica_cola
        self.politica_error_defecto = politica_error_defecto
        self.anti_inanicion_ratio_minimo = anti_inanicion_ratio_minimo
        self.sandbox_habilitar_whitelist = sandbox_habilitar_whitelist
        self.sondeo_umbral_inactividad = sondeo_umbral_inactividad
        self.max_memoria = max_memoria
        self.max_registros_por_archivo = max_registros_por_archivo
        self.frecuencia_percepcion = frecuencia_percepcion
        self.ciclo_timeout_segundos = ciclo_timeout_segundos
        self.protocol_strict_mode = protocol_strict_mode
        self.auto_start = auto_start
        if isinstance(agent_profile, dict):
            agent_profile = AgentProfile.desde_diccionario(agent_profile)
        self.agent_profile = agent_profile
        # Conditional component flags
        self.enable_circuit_breaker = enable_circuit_breaker
        self.enable_rate_limiter = enable_rate_limiter
        self.enable_auditor = enable_auditor
        self.enable_sandbox = enable_sandbox
        self.enable_health_probes = enable_health_probes
        self.enable_anti_starvation = enable_anti_starvation
    
    def validar(self) -> bool:
        if self.capacidad <= 0:
            raise ValueError("Capacidad debe ser > 0")
        if self.max_reintentos < 0:
            raise ValueError("Max reintentos no puede ser negativo")
        if not (0.0 < self.backpressure_threshold <= 1.0):
            raise ValueError("Backpressure threshold debe estar entre 0 y 1")
        if self.rate_limit_tokens <= 0:
            raise ValueError("Rate limit tokens debe ser > 0")
        if self.payload_max_bytes <= 0:
            raise ValueError("Payload max bytes debe ser > 0")
        if self.max_task_bytes <= 0:
            raise ValueError("Max task bytes debe ser > 0")
        if self.max_result_bytes <= 0:
            raise ValueError("Max result bytes debe ser > 0")
        if self.payload_overflow_policy not in ["truncate", "reject", "store_ref"]:
            raise ValueError("payload_overflow_policy inválida")
        if self.task_timeout_seconds is not None and self.task_timeout_seconds <= 0:
            raise ValueError("task_timeout_seconds debe ser > 0")
        if self.handler_timeout_seconds is not None and self.handler_timeout_seconds <= 0:
            raise ValueError("handler_timeout_seconds debe ser > 0")
        if not isinstance(self.renew_ttl_on_retry, bool):
            raise ValueError("renew_ttl_on_retry debe ser bool")
        if self.metricas_ventana_segundos <= 0:
            raise ValueError("Ventana de métricas debe ser > 0")
        if self.anti_inanicion_ratio_minimo <= 0 or self.anti_inanicion_ratio_minimo > 1:
            raise ValueError("Ratio anti inanición debe estar entre 0 y 1")
        if self.sondeo_umbral_inactividad <= 0:
            raise ValueError("Umbral de inactividad debe ser > 0")
        if self.max_memoria <= 0:
            raise ValueError("Max memoria debe ser > 0")
        if self.max_registros_por_archivo <= 0:
            raise ValueError("Max registros por archivo debe ser > 0")
        if self.frecuencia_percepcion <= 0:
            raise ValueError("Frecuencia de percepción debe ser > 0")
        if self.ciclo_timeout_segundos <= 0:
            raise ValueError("ciclo_timeout_segundos debe ser > 0")
        if not isinstance(self.auto_start, bool):
            raise ValueError("auto_start debe ser bool")
        if self.agent_profile is not None:
            if not isinstance(self.agent_profile, AgentProfile):
                raise ValueError("agent_profile debe ser AgentProfile o None")
            self.agent_profile.validar()
        return True
    
    def a_diccionario(self) -> Dict[str, Any]:
        return {
            "version": self.version,
            "capacidad": self.capacidad,
            "max_reintentos": self.max_reintentos,
            "ttl_defecto": self.ttl_defecto,
            "backpressure_threshold": self.backpressure_threshold,
            "max_workers": self.max_workers,
            "circuit_breaker_threshold": self.circuit_breaker_threshold,
            "rate_limit_tokens": self.rate_limit_tokens,
            "rate_limit_capacidad": self.rate_limit_capacidad,
            "payload_max_bytes": self.payload_max_bytes,
            "max_task_bytes": self.max_task_bytes,
            "max_result_bytes": self.max_result_bytes,
            "payload_overflow_policy": self.payload_overflow_policy,
            "task_timeout_seconds": self.task_timeout_seconds,
            "handler_timeout_seconds": self.handler_timeout_seconds,
            "renew_ttl_on_retry": self.renew_ttl_on_retry,
            "metricas_ventana_segundos": self.metricas_ventana_segundos,
            "politica_cola": self.politica_cola.value,
            "politica_error_defecto": self.politica_error_defecto.value,
            "anti_inanicion_ratio_minimo": self.anti_inanicion_ratio_minimo,
            "sandbox_habilitar_whitelist": self.sandbox_habilitar_whitelist,
            "sondeo_umbral_inactividad": self.sondeo_umbral_inactividad,
            "max_memoria": self.max_memoria,
            "max_registros_por_archivo": self.max_registros_por_archivo,
            "frecuencia_percepcion": self.frecuencia_percepcion,
            "ciclo_timeout_segundos": self.ciclo_timeout_segundos,
            "protocol_strict_mode": self.protocol_strict_mode,
            "auto_start": self.auto_start,
            "enable_circuit_breaker": self.enable_circuit_breaker,
            "enable_rate_limiter": self.enable_rate_limiter,
            "enable_auditor": self.enable_auditor,
            "enable_sandbox": self.enable_sandbox,
            "enable_health_probes": self.enable_health_probes,
            "enable_anti_starvation": self.enable_anti_starvation,
            "agent_profile": self.agent_profile.a_diccionario() if self.agent_profile else None,
            "timestamp_creacion": self.timestamp_creacion
        }
    
    @classmethod
    def desde_diccionario(cls, datos: Dict[str, Any]) -> 'ConfigAgente':
        import inspect
        sig = inspect.signature(cls.__init__)
        valid_params = set(sig.parameters.keys()) - {'self'}
        params = {k: v for k, v in datos.items() 
                 if k in valid_params and k not in ['version', 'timestamp_creacion']}
        if isinstance(params.get("politica_cola"), str):
            try:
                params["politica_cola"] = PoliticaCola(params["politica_cola"])
            except ValueError:
                params["politica_cola"] = PoliticaCola[params["politica_cola"].upper()]
        if isinstance(params.get("agent_profile"), dict):
            params["agent_profile"] = AgentProfile.desde_diccionario(params["agent_profile"])
        if isinstance(params.get("politica_error_defecto"), str):
            try:
                params["politica_error_defecto"] = PoliticaError(params["politica_error_defecto"])
            except ValueError:
                params["politica_error_defecto"] = PoliticaError[params["politica_error_defecto"].upper()]
        return cls(**params)

