# ================================
# routes/message_routes.py
# ================================
import json
import os
import logging
import redis.asyncio as redis
from fastapi import APIRouter
from dotenv import load_dotenv

from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain.memory import ConversationBufferMemory
from langchain_community.chat_message_histories import RedisChatMessageHistory
from langchain_mcp_adapters.client import MultiServerMCPClient

from models.dtos import DESCRIPCIONES_CREDITOS, REQUERIMIENTOS, STICKER_IDS, MessageRequest, MessageResponse
from agents.lista_agentes import obtener_agentes
from agents.deteccion import get_intencion_prompt

# --------------------------------
# CONFIGURACIÓN
# --------------------------------
load_dotenv()
logger = logging.getLogger(__name__)

SESSION_TTL = 180
MCP_URL = os.getenv("MCP_URL")
REDIS_URL = os.getenv("REDIS_URL")
OPENAI_KEY = os.getenv("OPENAI_API_KEY")

AGENTE = "intencion"
ESPECIALES = {"finalizado", "inicio", "asesor"}

redis_client = redis.from_url(REDIS_URL, decode_responses=True)
AGENTES = {}
all_tools = []

llm = ChatOpenAI(model="gpt-4o", temperature=0.0, api_key=OPENAI_KEY)

router = APIRouter()


# ============================================
# MCP INIT
# ============================================
async def inicializar_mcp():
    global all_tools, AGENTES

    try:
        client = MultiServerMCPClient({AGENTE: {"url": MCP_URL, "transport": "streamable_http"}})
        all_tools = await client.get_tools()
        AGENTES = obtener_agentes(all_tools)

        logger.info("🔧 MCP listo")
        for nombre, datos in AGENTES.items():
            tools = [t.name for t in datos["tools"]] if datos["tools"] else []
            logger.info(f"  - {nombre} → {tools}")

    except Exception as e:
        logger.error(f"⚠️ Error inicializando MCP: {e}")


# ============================================
# MEMORIA
# ============================================
def crear_memoria(whatsapp: str):
    history = RedisChatMessageHistory(url=REDIS_URL, session_id=f"session:{whatsapp}")
    return ConversationBufferMemory(
        memory_key="chat_history",
        chat_memory=history,
        return_messages=True
    )


# ============================================
# UTILIDADES
# ============================================
async def obtener_estado_sesion(whatsapp: str):
    session_key = f"session:{whatsapp}"
    memoria_key = f"message_store:{session_key}"

    sesion_existe = bool(await redis_client.exists(session_key))
    estado = await redis_client.hget(session_key, "estado") if sesion_existe else "inicio"

    if sesion_existe:
        await redis_client.expire(session_key, SESSION_TTL)

    return sesion_existe, estado, session_key, memoria_key


async def renovar_ttl(session_key: str, memoria_key: str, renovar_memoria=False):
    await redis_client.expire(session_key, SESSION_TTL)
    if renovar_memoria:
        await redis_client.expire(memoria_key, SESSION_TTL)


async def detectar_agente(mensaje: str, estado_actual: str):
    prompt = get_intencion_prompt()
    chain = prompt | llm
    lista_agentes = ", ".join(AGENTES.keys())

    result = await chain.ainvoke({
        "intenciones": lista_agentes,
        "intencion_actual": estado_actual,
        "input": mensaje
    })

    detectado = (getattr(result, "content", "") or "").strip().lower()
    logger.info(f"🔍 Detectado: {detectado}")

    if detectado not in AGENTES and detectado not in ESPECIALES:
        return estado_actual

    return detectado


async def limpiar_sesion(session_key: str, memoria_key: str):
    await redis_client.delete(session_key)
    await redis_client.delete(memoria_key)


async def manejar_asesor(whatsapp, session_key, memoria_key):
    await limpiar_sesion(session_key, memoria_key)
    return MessageResponse(
        whatsapp=whatsapp,
        respuesta="🙋 Te conecto con un asesor humano...",
        sticker_id=STICKER_IDS.get("asesor")
    )


async def manejar_finalizado(whatsapp, session_key, memoria_key):
    await limpiar_sesion(session_key, memoria_key)
    return MessageResponse(
        whatsapp=whatsapp,
        respuesta="¡Gracias por comunicarte! 😊",
        sticker_id=STICKER_IDS.get("finalizado")
    )


async def enviar_menu_inicio(whatsapp, session_key, memoria_key):
    await redis_client.hset(session_key, mapping={"estado": "inicio"})
    await renovar_ttl(session_key, memoria_key, renovar_memoria=True)

    texto = (
        "😊 ¡Estoy aquí para ayudarte a alcanzar tus sueños y metas! ✨\n"
        "Te ofrezco *Ahorros*, *Créditos*, *Inversiones* y más.\n\n"
        "Te puedo asistir con los siguientes servicios: 👇\n"
        "• Consultar saldo de ahorros\n"
        "• Bloquear tarjeta de débito\n"
        "• Solicitar créditos, consultar su cuota y fecha de pago\n"
        "• Consultar inversiones (Plazo fijo)\n\n"
        "Enlaces útiles:\n"
        "• App Mi23: https://link.app.mi23\n"
        "• Cooperativa Virtual: https://cooperativa23.com\n"
    )

    return MessageResponse(
        whatsapp=whatsapp,
        respuesta=texto,
        sticker_id=STICKER_IDS.get("inicio")
    )


async def actualizar_estado(session_key, memoria_key, antes, nuevo):
    await redis_client.hset(session_key, mapping={"estado": nuevo})
    await redis_client.delete(memoria_key)
    logger.info(f"🔄 Estado {antes} → {nuevo}")


async def ejecutar_agente(agente, mensaje, memoria, memoria_key):
    datos = AGENTES[agente]
    prompt = datos.get("prompt_func")()
    tools = datos.get("tools", [])
    
    # ---------------------------------------------
    # 💡 Lógica para Agents con Tools (consultar_saldo, inversiones, etc.)
    # ---------------------------------------------
    # Mantenemos la exclusión de 'creditos' para asegurar que usamos la cadena simple
    # y así inyectar las variables necesarias.
    if tools and agente not in ["creditos"]: 
        executor = AgentExecutor(
            agent=create_openai_functions_agent(llm=llm, tools=tools, prompt=prompt),
            tools=tools,
            memory=memoria,
            verbose=True
        )
        result = await executor.ainvoke({"input": mensaje})
        await redis_client.expire(memoria_key, SESSION_TTL)
        return result.get("output")
        
    # ---------------------------------------------
    # 💡 Lógica para Agents sin Tools o Agentes Forzados (como 'creditos')
    # ---------------------------------------------
    chain = (prompt | llm)
    history = memoria.load_memory_variables({})
    
    # Prepara el diccionario de inputs
    inputs = {
        "input": mensaje,
        "chat_history": history["chat_history"],
        "agent_scratchpad": []
    }
    
    # INYECTAR VARIABLES DE CONTEXTO solo si es el agente 'creditos'
    if agente == "creditos":
        # 1. Inyección de IDs de requerimiento
        inputs["requerimientos"] = json.dumps(REQUERIMIENTOS, indent=4) 
        
        # 2. Inyección de descripciones de créditos (NUEVA VARIABLE)
        inputs["descripciones_creditos"] = DESCRIPCIONES_CREDITOS # <-- AÑADIDO
        

    resp = await chain.ainvoke(inputs)

    content = getattr(resp, "content", str(resp)).strip()
    memoria.save_context({"input": mensaje}, {"output": content})
    await redis_client.expire(memoria_key, SESSION_TTL)

    return content

# ============================================
# ENDPOINT /message (Corregido)
# ============================================
@router.post("/message")
async def recibir_message(request: MessageRequest):
    whatsapp = request.whatsapp
    mensaje = request.mensaje.strip()

    logger.info(f"👤 [{whatsapp}]: {mensaje}")

    sesion_existe, estado_actual, session_key, memoria_key = await obtener_estado_sesion(whatsapp)
    sticker_id = ""
    
    # Inicializar el nuevo campo
    imagen_id = None 

    agente = await detectar_agente(mensaje, estado_actual)

    # 1. 🛑 MANEJO DE CASOS ESPECIALES (RETORNO INMEDIATO)
    if agente == "asesor":
        return await manejar_asesor(whatsapp, session_key, memoria_key)

    if agente == "finalizado":
        return await manejar_finalizado(whatsapp, session_key, memoria_key)

    if agente == "inicio":
        if estado_actual != "inicio":
            await actualizar_estado(session_key, memoria_key, estado_actual, agente)
            return await enviar_menu_inicio(whatsapp, session_key, memoria_key)
        return await enviar_menu_inicio(whatsapp, session_key, memoria_key)
    
    # 2. 🔄 LÓGICA DE AGENTE NORMAL (Flujo de conversación)

    # Cambio de agente: Asigna el sticker SÓLO si hay una transición de estado.
    if agente != estado_actual:
        await actualizar_estado(session_key, memoria_key, estado_actual, agente)
        sticker_id = STICKER_IDS.get(agente, "")

    # Si NO hay cambio de estado, renovamos el TTL para la memoria del agente.
    await renovar_ttl(session_key, memoria_key, renovar_memoria=(agente == estado_actual))

    memoria = crear_memoria(whatsapp)

    # Ejecución del Agente
    respuesta = await ejecutar_agente(agente, mensaje, memoria, memoria_key)
    logger.info(f"🤖 [{agente}]: {respuesta}")

    # ----------------------------------------------------
    # 💡 LÓGICA ESPECIAL PARA AGENTE CREDITOS (Paso 3)
    # ----------------------------------------------------
    if agente == "creditos" and respuesta.isdigit() and len(respuesta) > 10:
        # Si la respuesta es un ID de requerimiento numérico
        imagen_id = respuesta
        
        # Limpia la respuesta para que no aparezca el ID en el mensaje de texto
        respuesta = "Perfecto. Aquí tienes los requisitos para ese tipo de crédito."
        
        # ❌ ELIMINADA: Esta línea se elimina para evitar que el sticker se envíe por segunda vez
        # sticker_id = STICKER_IDS.get(agente, "") 

    return MessageResponse(
        whatsapp=whatsapp, 
        respuesta=respuesta, 
        sticker_id=sticker_id, # Usará el sticker asignado en la transición o ""
        imagen_id=imagen_id    # Nuevo campo retornado
    )