import httpx
import logging
import os
from datetime import datetime
from typing import Dict, Any
import json
import html
import re
import asyncio
from dotenv import load_dotenv

# Cargar variables de entorno
load_dotenv()

# ============================================================================
# CONFIGURACIÓN DE SEGURIDAD
# ============================================================================

# URL desde variable de entorno o fallback
WEBHOOK_URL = os.getenv("N8N_WEBHOOK_URL", "https://dev.n8n.catastroantioquia-mas.com/webhook/tasa-pru-web")
REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "120"))
MAX_RETRIES = int(os.getenv("MAX_RETRIES", "2"))

# Configurar logging específico
logger = logging.getLogger(__name__)

# ============================================================================
# VALIDACIÓN Y SANITIZACIÓN
# ============================================================================

def sanitize_input_basic(value: str) -> str:
    """Sanitización básica de entrada - Bloquea patrones maliciosos conocidos"""
    if not isinstance(value, str):
        raise ValueError("El valor debe ser una cadena de texto")
    
    # Patrones de inyección más comunes y críticos
    malicious_patterns = [
        r"(?i)(<script[^>]*>.*?</script>)",  # Scripts
        r"(?i)(javascript:|vbscript:)",      # Protocolos maliciosos
        r"(?i)(union\s+select|select\s+.*\s+from)",  # SQL injection básico
        r"(?i)(or\s+1\s*=\s*1|and\s+1\s*=\s*1)",    # SQL injection clásico
        r"(?i)(<iframe|<object|<embed)",      # Elementos HTML peligrosos
        r"(\.\./){2,}",                       # Path traversal
        # Caracteres especiales más selectivos
        r"[<>\"'`](?=.*[{}[\]\\;|&])"        # Combinaciones peligrosas
    ]
    
    for pattern in malicious_patterns:
        if re.search(pattern, value):
            logger.warning(f"Patrón malicioso bloqueado en entrada")
            raise ValueError("Contenido no permitido detectado")
    
    # Limpiar y escapar
    cleaned = html.escape(value.strip())
    
    # Validar longitud
    if len(cleaned) > 2000:  # Aumentado para preguntas más largas
        raise ValueError("Contenido demasiado largo")
    
    return cleaned

def validate_webhook_response(response_data: Dict[str, Any]) -> bool:
    """Validar que la respuesta del webhook no contenga contenido malicioso"""
    try:
        response_str = json.dumps(response_data)
        
        # Patrones maliciosos en respuestas
        dangerous_patterns = [
            r'(?i)(<script[^>]*>.*?</script>)',
            r'(?i)(javascript:|vbscript:)',
            r'(?i)(on\w+\s*=)',  # Eventos JS
            r'(?i)(<iframe|<object|<embed)'
        ]
        
        for pattern in dangerous_patterns:
            if re.search(pattern, response_str):
                logger.warning("Respuesta del webhook contiene contenido peligroso")
                return False
        
        return True
    except Exception as e:
        logger.error(f"Error validando respuesta: {e}")
        return False

# ============================================================================
# FUNCIÓN PRINCIPAL CON SEGURIDAD
# ============================================================================

async def enviar_chat_n8n(datos: dict) -> dict:
    """
    Función principal con medidas de seguridad implementadas
    """
    try:
        # VALIDACIÓN INICIAL
        session_id = (datos.get("sessionId") or "").strip()
        pregunta = (datos.get("pregunta") or "").strip()
        audio_enabled = datos.get("audio_enabled", False)

        if not session_id or not pregunta:
            logger.warning("Campos requeridos faltantes")
            return create_safe_error("Faltan campos sessionId y pregunta")

        # SANITIZACIÓN DE ENTRADA
        try:
            clean_session_id = sanitize_input_basic(session_id)
            clean_pregunta = sanitize_input_basic(pregunta)
        except ValueError as e:
            logger.warning(f"Entrada rechazada por seguridad: {e}")
            return create_safe_error("Datos de entrada no válidos")

        # VALIDACIONES ADICIONALES
        if len(clean_session_id) > 100:
            return create_safe_error("sessionId demasiado largo")

        # CONSTRUCCIÓN SEGURA DEL PAYLOAD
        payload = {
            "sessionId": clean_session_id,
            "pregunta": clean_pregunta,
            "audio_enabled": bool(audio_enabled),
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

        # VERIFICAR CONFIGURACIÓN
        if not WEBHOOK_URL:
            logger.error("URL del webhook no configurada")
            return create_safe_error("Configuración de servicio no encontrada")

        # LOGGING DE SEGURIDAD (sin exponer datos sensibles)
        logger.info(f"Procesando solicitud para sesión: {clean_session_id[:8]}***")

        # REALIZAR SOLICITUD CON REINTENTOS Y TIMEOUT
        response_data = None
        last_error = None

        for attempt in range(MAX_RETRIES):
            try:
                async with httpx.AsyncClient(
                    timeout=httpx.Timeout(REQUEST_TIMEOUT),
                    limits=httpx.Limits(max_connections=5)
                ) as client:
                    
                    response = await client.post(
                        WEBHOOK_URL,
                        json=payload,
                        headers={
                            "Content-Type": "application/json",
                            "Accept": "application/json",
                            "User-Agent": "SecureChatAPI/1.0"
                        }
                    )

                # MANEJO DE CÓDIGOS DE ESTADO
                if response.status_code == 429:  # Rate limited
                    logger.warning("Rate limit del webhook alcanzado")
                    if attempt < MAX_RETRIES - 1:
                        await asyncio.sleep(2 ** attempt)  # Backoff exponencial
                        continue
                    return create_safe_error("Servicio temporalmente ocupado")

                if response.status_code != 200:
                    logger.error(f"Error HTTP del webhook: {response.status_code}")
                    return create_safe_error(f"Error del servicio externo: {response.status_code}")

                # PARSEAR RESPUESTA CON VALIDACIÓN
                try:
                    response_data = response.json()
                except json.JSONDecodeError:
                    logger.error("Respuesta JSON inválida del webhook")
                    return create_safe_error("Respuesta inválida del servicio")

                # VALIDACIÓN DE SEGURIDAD DE LA RESPUESTA
                if not validate_webhook_response(response_data):
                    logger.warning("Respuesta del webhook falló validación de seguridad")
                    return create_safe_error("Respuesta no válida del servicio")

                # Éxito - salir del loop de reintentos
                break

            except httpx.TimeoutException:
                last_error = "Timeout del servicio"
                logger.warning(f"Timeout en intento {attempt + 1}")
            except httpx.RequestError as e:
                last_error = f"Error de conexión: {str(e)}"
                logger.warning(f"Error de conexión en intento {attempt + 1}: {e}")

            # Esperar antes del siguiente intento
            if attempt < MAX_RETRIES - 1:
                await asyncio.sleep(1)

        # VERIFICAR SI TODOS LOS INTENTOS FALLARON
        if response_data is None:
            logger.error(f"Todos los reintentos fallaron: {last_error}")
            return create_safe_error("Servicio no disponible")

        # PREPARAR RESPUESTA SEGURA
        safe_response = create_safe_success(
            message=response_data.get("message", "Respuesta procesada correctamente"),
            data=sanitize_response_data(response_data)
        )

        logger.info(f"Solicitud procesada exitosamente para sesión: {clean_session_id[:8]}***")
        return safe_response

    except Exception as e:
        logger.exception("Error inesperado en enviar_chat_n8n")
        return create_safe_error("Error al procesar el mensaje de chat")

# ============================================================================
# FUNCIONES DE UTILIDAD SEGURAS
# ============================================================================

def create_safe_error(message: str) -> dict:
    """Crear respuesta de error segura"""
    return {
        "success": False,
        "message": html.escape(str(message)[:200])  # Limitar longitud
    }

def create_safe_success(message: str, data: Any = None) -> dict:
    """Crear respuesta exitosa segura"""
    response = {
        "success": True,
        "message": html.escape(str(message)[:200])
    }
    
    if data is not None:
        response["data"] = data
    
    return response

def sanitize_response_data(data: Any) -> Any:
    """Sanitizar datos de respuesta para prevenir XSS"""
    if isinstance(data, str):
        return html.escape(data)
    elif isinstance(data, dict):
        # Sanitizar solo campos de texto, preservar estructura
        sanitized = {}
        for key, value in data.items():
            if isinstance(value, str):
                sanitized[key] = html.escape(value)
            elif isinstance(value, (dict, list)):
                sanitized[key] = sanitize_response_data(value)
            else:
                sanitized[key] = value
        return sanitized
    elif isinstance(data, list):
        return [sanitize_response_data(item) for item in data]
    return data
