"""
Servicio de flujos multi-paso: redirect a agente, consulta de saldo y opciones de pago.
"""
import json as json_mod
import logging

from langchain_core.messages import (
    HumanMessage as LCHumanMessage,
    AIMessage as LCAIMessage,
    SystemMessage,
)
from langchain_openai import ChatOpenAI
from mcp_integration.tools import get_tools
from agent.chat_products_nodes import _extract_mcp_result
from agent.prompts import BALANCE_PROMPT, PAYMENT_PROMPT, FAQ_PROMPT, FAQ_MARCIMEX
from config.settings import settings
from ..models import SearchChatResponse
from core.session_manager import session_manager
from .identity_service import IdentityService

logger = logging.getLogger(__name__)

REDIRECT_CTX_TTL = 600   # 10 minutos para el flujo de redirección
BALANCE_CTX_TTL = 600    # 10 minutos para el flujo de consulta de saldo
MAX_TOTAL_HISTORY = 20

_llm = ChatOpenAI(model=settings.MODEL_NAME, temperature=0)


class FlowService:
    """Máquinas de estado multi-paso para redirect a agente, balance y pago."""

    def __init__(self, identity_service: IdentityService):
        self.identity = identity_service

    # -------------------------------------------------------------------------
    # FLUJO DE REDIRECCIÓN A AGENTE
    # -------------------------------------------------------------------------

    async def handle_redirect_flow(
        self,
        uuid: str,
        redirect_ctx: dict,
        query: str,
    ) -> SearchChatResponse | None:
        """
        Maneja el flujo multi-paso de redirección a agente humano.
        Pasos: awaiting_cedula → awaiting_tipo_pago → (si Crédito) awaiting_fecha_nacimiento → MCP
        Tras 2 intentos fallidos en cualquier paso se llama redirect_to_agent_direct.
        """
        REDIRECT_CTX_KEY = f"search_redirect_ctx:{uuid}"
        step = redirect_ctx.get("step")
        platform_id = redirect_ctx.get("platform_id")
        usuario = redirect_ctx.get("usuario")

        session = await session_manager.get_session(uuid)
        history_messages = session.state.get("messages", [])

        if step == "awaiting_cedula":
            cedula = query.strip()

            if not self.identity.validate_cedula(cedula):
                attempts = redirect_ctx.get("cedula_attempts", 0) + 1
                logger.warning(f"⚠️ [{uuid}] Cédula inválida en redirect: '{cedula}' (intento {attempts})")
                if attempts >= 2:
                    await session_manager.client.delete(REDIRECT_CTX_KEY)
                    logger.info(f"🚨 [{uuid}] Cédula inválida {attempts} veces — redirigiendo a asesor directo")
                    return await self.invoke_redirect_to_agent_direct(
                        uuid, query, history_messages, session, platform_id, usuario
                    )
                redirect_ctx["cedula_attempts"] = attempts
                await session_manager.client.setex(REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx))
                answer = "La cédula ingresada no es válida. Por favor, verifica e ingresa tu número de cédula correctamente:"
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)
                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
                )

            redirect_ctx["cedula"] = cedula
            redirect_ctx["step"] = "awaiting_tipo_pago"
            await session_manager.client.setex(
                REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx)
            )
            logger.info(f"📋 [{uuid}] Cédula recibida: {cedula}. Pidiendo tipo de pago.")
            answer = "¡Gracias! 😊\n\nAhora, por favor selecciona el tipo de pago:\n\n1️⃣ Crédito Directo\n2️⃣ Efectivo / Tarjetas\n\nEscribe el número o el nombre de la opción para continuar:"
            history_messages.append(LCHumanMessage(content=query))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            return SearchChatResponse(
                uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
            )

        elif step == "awaiting_tipo_pago":
            raw = query.strip().lower()

            if raw in ("1", "credito directo", "crédito directo", "credito", "crédito"):
                tipo_pago = "Credito Directo"
            elif raw in ("2", "efectivo/tarjetas", "efectivo", "tarjetas", "tarjeta", "efectivo tarjetas"):
                tipo_pago = "Efectivo/Tarjetas"
            else:
                attempts = redirect_ctx.get("tipo_pago_attempts", 0) + 1
                logger.warning(f"⚠️ [{uuid}] Tipo de pago no reconocido: '{query.strip()}' (intento {attempts})")
                if attempts >= 2:
                    await session_manager.client.delete(REDIRECT_CTX_KEY)
                    logger.info(f"🚨 [{uuid}] Tipo de pago no reconocido {attempts} veces — redirigiendo a asesor directo")
                    return await self.invoke_redirect_to_agent_direct(
                        uuid, query, history_messages, session, platform_id, usuario
                    )
                redirect_ctx["tipo_pago_attempts"] = attempts
                await session_manager.client.setex(REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx))
                answer = "No reconocí esa opción. Por favor selecciona el tipo de pago:\n\n1. Crédito Directo\n2. Efectivo/Tarjetas\n\nEscribe el número o el nombre de la opción:"
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)
                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
                )

            cedula = redirect_ctx.get("cedula", "")

            if tipo_pago == "Credito Directo":
                saved_fecha = redirect_ctx.get("fecha_nacimiento_saved")
                if saved_fecha:
                    logger.info(f"🔑 [{uuid}] Tipo de pago: {tipo_pago}. Reutilizando fecha guardada: {saved_fecha}")
                    fecha_api = saved_fecha.replace("-", "/")
                    await session_manager.client.delete(REDIRECT_CTX_KEY)
                    return await self._invoke_redirect_to_agent(
                        uuid, cedula, tipo_pago, fecha_api, query, history_messages, session,
                        platform_id, usuario,
                    )

                redirect_ctx["tipo_pago"] = tipo_pago
                redirect_ctx["step"] = "awaiting_fecha_nacimiento"
                await session_manager.client.setex(
                    REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx)
                )
                logger.info(f"📋 [{uuid}] Tipo de pago: {tipo_pago}. Pidiendo fecha de nacimiento.")

                answer = "Para Crédito Directo 💳 necesito tu fecha de nacimiento 📅\n\nEjemplo: 25/07/1997"
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)

                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
                )
            else:
                await session_manager.client.delete(REDIRECT_CTX_KEY)
                return await self._invoke_redirect_to_agent(
                    uuid, cedula, tipo_pago, None, query, history_messages, session,
                    platform_id, usuario,
                )

        elif step == "awaiting_fecha_nacimiento":
            raw_fecha = query.strip()
            cedula = redirect_ctx.get("cedula", "")
            tipo_pago = redirect_ctx.get("tipo_pago", "Credito Directo")

            fecha_normalizada = self.identity.parse_date(raw_fecha)
            if not fecha_normalizada:
                attempts = redirect_ctx.get("fecha_attempts", 0) + 1
                logger.warning(f"⚠️ [{uuid}] Fecha no válida: '{raw_fecha}' (intento {attempts})")
                if attempts >= 2:
                    await session_manager.client.delete(REDIRECT_CTX_KEY)
                    logger.info(f"🚨 [{uuid}] Fecha invalida {attempts} veces — redirigiendo a asesor directo")
                    return await self.invoke_redirect_to_agent_direct(
                        uuid, query, history_messages, session, platform_id, usuario
                    )
                redirect_ctx["fecha_attempts"] = attempts
                await session_manager.client.setex(REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx))
                answer = "No pude reconocer la fecha ingresada. Por favor, ingrésala nuevamente (ejemplo: 25 de julio de 1997 o 25-07-1997):"
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)
                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
                )

            fecha_api = fecha_normalizada.replace("-", "/")
            await session_manager.client.delete(REDIRECT_CTX_KEY)
            logger.info(f"📋 [{uuid}] Fecha de nacimiento: {fecha_api}. Invocando redirect_to_agent.")
            return await self._invoke_redirect_to_agent(
                uuid, cedula, tipo_pago, fecha_api, query, history_messages, session,
                platform_id, usuario,
            )

        return None

    def _build_resumen(self, history_messages: list) -> str:
        """Genera un resumen completo de la conversación con emojis y análisis emocional del usuario."""
        default = "Usuario solicito que se le asigne un asesor"
        if not history_messages:
            return default
        try:
            lines = []
            for msg in history_messages:
                if isinstance(msg, LCHumanMessage):
                    lines.append(f"Usuario: {msg.content}")
                elif isinstance(msg, LCAIMessage):
                    lines.append(f"Asistente: {msg.content[:300]}")
            conversation_text = "\n".join(lines).strip()
            if not conversation_text:
                return default
            response = _llm.invoke([
            SystemMessage(content=(
                "Eres un analista experto en experiencia de cliente. Analiza la conversación entre un usuario y un asistente de ventas.\n\n"
                "Genera un resumen BREVE que incluya:\n"
                "1. 🛒 Productos e intereses: productos, categorías, marcas, precios o características mencionadas.\n"
                "2. 📋 Resumen de la conversación: breve y claro, máximo 3-4 líneas.\n"
                "3. 😊 Análisis emocional: emociones detectadas con porcentajes y emojis.\n"
                "4. 💡 Necesidad principal: en una sola oración.\n\n"
                "Usa emojis de forma natural y responde en español.\n\n"
                f"CONVERSACIÓN COMPLETA:\n{conversation_text}\n\nRESUMEN:"
            ))
            ])
            summary = response.content.strip()
            return summary if summary else default
        except Exception:
            return default

    async def _invoke_redirect_to_agent(
        self,
        uuid: str,
        cedula: str,
        tipo_pago: str,
        fecha_nacimiento: str | None,
        query: str,
        history_messages: list,
        session,
        platform_id: str | None = None,
        usuario: str | None = None,
    ) -> SearchChatResponse:
        """Invoca el tool redirect_to_agent via MCP, guarda identidad y retorna respuesta."""
        await self.identity.save_identity(session_manager.client, uuid, cedula, fecha_nacimiento)
        answer = "REDIRECTION_ASESOR_BOT"
        resumen = self._build_resumen(history_messages)
        try:
            tools = get_tools()
            redirect_tool = next((t for t in tools if t.name == "redirect_to_agent"), None)

            if redirect_tool:
                logger.info(f"📡 [{uuid}] Invocando redirect_to_agent via MCP...")
                params = {"cedula": cedula, "tipo_pago": tipo_pago, "resumen_conversacion": resumen}
                if platform_id:
                    params["platformId"] = platform_id
                if usuario:
                    params["usuario"] = usuario
                if fecha_nacimiento:
                    params["fecha_nacimiento"] = fecha_nacimiento
                result = redirect_tool.invoke(params)
                logger.info(f"📥 [{uuid}] Resultado MCP redirect: {str(result)[:300]}")
            else:
                logger.warning(f"⚠️ [{uuid}] Tool redirect_to_agent no encontrado")

        except Exception as e:
            logger.error(f"❌ [{uuid}] Error en redirect_to_agent: {e}")

        history_messages.append(LCHumanMessage(content=query))
        history_messages.append(LCAIMessage(content=answer))
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid,
            answer=answer,
            intent="AGENT",
            total=0,
            redirect_asesor=True,
            summary_agent=resumen,
            products=[],
        )

    async def init_redirect_flow(
        self,
        uuid: str,
        question: str,
        history_messages: list,
        session,
        existing_identity: dict,
        platform_id: str | None = None,
        usuario: str | None = None,
    ) -> SearchChatResponse:
        """Inicializa el flujo AGENT, reutilizando cédula guardada si está disponible."""
        REDIRECT_CTX_KEY = f"search_redirect_ctx:{uuid}"
        saved_cedula = existing_identity.get("cedula")
        saved_fecha = existing_identity.get("fecha_nacimiento")

        if saved_cedula:
            logger.info(f"🔑 [{uuid}] Reutilizando cédula existente: {saved_cedula}")
            answer = "Ahora, ¿cómo te gustaría realizar el pago? 💳\n\n1️⃣ Crédito Directo\n2️⃣ Efectivo / Tarjetas"
            redirect_ctx: dict = {"step": "awaiting_tipo_pago", "cedula": saved_cedula}
            if saved_fecha:
                redirect_ctx["fecha_nacimiento_saved"] = saved_fecha
            if platform_id:
                redirect_ctx["platform_id"] = platform_id
            if usuario:
                redirect_ctx["usuario"] = usuario
            await session_manager.client.setex(
                REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx)
            )
            history_messages.append(LCHumanMessage(content=question))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            return SearchChatResponse(
                uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
            )

        answer = "Para asignarte un asesor lo más pronto posible, necesito validar un dato 😊\n¿Me compartes tu número de cédula, por favor?"
        redirect_ctx = {"step": "awaiting_cedula"}
        if platform_id:
            redirect_ctx["platform_id"] = platform_id
        if usuario:
            redirect_ctx["usuario"] = usuario
        await session_manager.client.setex(
            REDIRECT_CTX_KEY, REDIRECT_CTX_TTL, json_mod.dumps(redirect_ctx)
        )
        history_messages.append(LCHumanMessage(content=question))
        history_messages.append(LCAIMessage(content=answer))
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid, answer=answer, intent="AGENT", total=0, products=[]
        )

    async def invoke_redirect_to_agent_direct(
        self,
        uuid: str,
        query: str,
        history_messages: list,
        session,
        platform_id: str | None = None,
        usuario: str | None = None,
    ) -> SearchChatResponse:
        """
        Invoca redirect_to_agent_direct via MCP para usuarios frustrados o atascados.
        No requiere flujo multi-paso: solo necesita platformId y usuario.
        """
        answer = "REDIRECTION_ASESOR_BOT"
        resumen = self._build_resumen(history_messages)
        try:
            tools = get_tools()
            redirect_tool = next((t for t in tools if t.name == "redirect_to_agent_direct"), None)

            if redirect_tool:
                logger.info(f"📡 [{uuid}] Invocando redirect_to_agent_direct via MCP...")
                logger.info(f"🔎 [{uuid}] Datos recibidos → platform_id='{platform_id}' | usuario='{usuario}'")
                params = {"resumen_conversacion": resumen}
                if platform_id:
                    params["platformId"] = platform_id
                if usuario:
                    params["usuario"] = usuario
                result = redirect_tool.invoke(params)
                logger.info(f"📥 [{uuid}] Resultado MCP redirect_direct: {str(result)[:300]}")
            else:
                logger.warning(f"⚠️ [{uuid}] Tool redirect_to_agent_direct no encontrado")

        except Exception as e:
            logger.error(f"❌ [{uuid}] Error en redirect_to_agent_direct: {e}")

        history_messages.append(LCHumanMessage(content=query))
        history_messages.append(LCAIMessage(content=answer))
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid,
            answer=answer,
            intent="AGENT_DIRECT",
            total=0,
            redirect_asesor=True,
            summary_agent=resumen,
            products=[],
        )

    # -------------------------------------------------------------------------
    # FLUJO DE FAQ (PREGUNTAS FRECUENTES)
    # -------------------------------------------------------------------------

    async def handle_faq(
        self,
        uuid: str,
        query: str,
        history_messages: list,
        session,
    ) -> SearchChatResponse:
        """Responde preguntas generales usando el FAQ embebido directamente en el prompt."""
        logger.info(f"❓ [{uuid}] Manejando pregunta FAQ: '{query[:80]}'")

        has_history = len(history_messages) > 0
        no_greeting_rule = (
            "\nIMPORTANTE: Ya hay conversación previa. NO saludes ni digas 'Hola'. Ve directo a la respuesta."
            if has_history else ""
        )

        system_prompt = FAQ_PROMPT.format(
            no_greeting_rule=no_greeting_rule,
            faq_content=FAQ_MARCIMEX,
        )

        recent_history = history_messages[-10:] if len(history_messages) > 10 else history_messages
        # Strip [CONTEXTO DEL SISTEMA] from AI messages to prevent the LLM from
        # hallucinating product listings with fake IDs in the FAQ response.
        cleaned_history = []
        for m in recent_history:
            if isinstance(m, LCHumanMessage):
                cleaned_history.append(m)
            else:
                content = getattr(m, "content", "")
                if "[CONTEXTO DEL SISTEMA" in content:
                    content = content.split("[CONTEXTO DEL SISTEMA")[0].strip()
                cleaned_history.append(LCAIMessage(content=content))
        llm_messages = [SystemMessage(content=system_prompt)]
        llm_messages.extend(cleaned_history)
        llm_messages.append(LCHumanMessage(content=query))

        answer = "En este momento no tengo esa información disponible. Te recomiendo contactar a uno de nuestros asesores. 😊"
        try:
            response = _llm.invoke(llm_messages)
            answer = response.content.strip() or answer
            logger.info(f"✅ [{uuid}] Respuesta FAQ generada")
        except Exception as e:
            logger.error(f"❌ [{uuid}] Error en handle_faq: {e}")

        history_messages.append(LCHumanMessage(content=query))
        history_messages.append(LCAIMessage(content=answer))
        if len(history_messages) > MAX_TOTAL_HISTORY:
            history_messages = history_messages[-MAX_TOTAL_HISTORY:]
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid,
            answer=answer,
            intent="OTHER",
            total=0,
            products=[],
        )

    # -------------------------------------------------------------------------
    # FLUJO DE CONSULTA DE SALDO
    # -------------------------------------------------------------------------

    async def handle_balance_flow(
        self,
        uuid: str,
        balance_ctx: dict,
        query: str,
    ) -> SearchChatResponse | None:
        """
        Maneja el flujo multi-paso de consulta de saldo.
        Pasos: awaiting_cedula → awaiting_fecha → invoca consultar_deuda_actual via MCP.
        Tras 2 intentos fallidos en cualquier paso se llama redirect_to_agent_direct.
        """
        BALANCE_CTX_KEY = f"search_balance_ctx:{uuid}"
        step = balance_ctx.get("step")
        platform_id = balance_ctx.get("platform_id")
        usuario = balance_ctx.get("usuario")

        session = await session_manager.get_session(uuid)
        history_messages = session.state.get("messages", [])

        if step == "awaiting_cedula":
            cedula = query.strip()

            if not self.identity.validate_cedula(cedula):
                attempts = balance_ctx.get("cedula_attempts", 0) + 1
                logger.warning(f"⚠️ [{uuid}] Cédula inválida: '{cedula}' (intento {attempts})")
                if attempts >= 2:
                    await session_manager.client.delete(BALANCE_CTX_KEY)
                    logger.info(f"🚨 [{uuid}] Cédula inválida {attempts} veces — redirigiendo a asesor directo")
                    return await self.invoke_redirect_to_agent_direct(
                        uuid, query, history_messages, session, platform_id, usuario
                    )
                balance_ctx["cedula_attempts"] = attempts
                await session_manager.client.setex(BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx))
                answer = "La cédula ingresada no es válida. Por favor, verifica e ingresa tu número de cédula correctamente:"
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)
                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
                )

            # Validación adicional via MCP
            try:
                tools = get_tools()
                validar_tool = next((t for t in tools if t.name == "validar_cedula"), None)

                if validar_tool:
                    val_result = validar_tool.invoke({"nro_ide": cedula})
                    val_data = _extract_mcp_result(val_result)

                    if isinstance(val_data, dict) and not val_data.get("valida", False):
                        attempts = balance_ctx.get("cedula_attempts", 0) + 1
                        mensaje_tecnico = val_data.get("mensaje", "")
                        logger.warning(f"⚠️ [{uuid}] Cédula inválida por MCP: {cedula} - {mensaje_tecnico} (intento {attempts})")
                        if attempts >= 2:
                            await session_manager.client.delete(BALANCE_CTX_KEY)
                            logger.info(f"🚨 [{uuid}] Cédula inválida por MCP {attempts} veces — redirigiendo a asesor directo")
                            return await self.invoke_redirect_to_agent_direct(
                                uuid, query, history_messages, session, platform_id, usuario
                            )
                        balance_ctx["cedula_attempts"] = attempts
                        await session_manager.client.setex(BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx))
                        answer = "La cédula ingresada no es válida. Por favor, verifica e ingresa tu número de cédula correctamente:"
                        history_messages.append(LCHumanMessage(content=query))
                        history_messages.append(LCAIMessage(content=answer))
                        session.state["messages"] = history_messages
                        await session_manager.save_session(uuid, session)
                        return SearchChatResponse(
                            uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
                        )
                    logger.info(f"✅ [{uuid}] Cédula válida: {cedula}")
                else:
                    logger.warning(f"⚠️ [{uuid}] Tool validar_cedula no encontrado, continuando con validación básica.")
            except Exception as e:
                logger.error(f"❌ [{uuid}] Error validando cédula via MCP: {e}. Continuando con validación básica.")

            balance_ctx["nro_ide"] = cedula
            balance_ctx["step"] = "awaiting_fecha"
            await session_manager.client.setex(
                BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx)
            )
            logger.info(f"💰 [{uuid}] Cédula recibida: {cedula}. Pidiendo fecha de nacimiento.")

            answer = "¡Gracias! 😊\nSolo me falta tu fecha de nacimiento para poder para continuar."
            history_messages.append(LCHumanMessage(content=query))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            return SearchChatResponse(
                uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
            )

        elif step == "awaiting_fecha":
            raw_fecha = query.strip()
            nro_ide = balance_ctx.get("nro_ide", "")

            fecha_nacimiento = self.identity.parse_date(raw_fecha)
            if not fecha_nacimiento:
                attempts = balance_ctx.get("fecha_attempts", 0) + 1
                logger.warning(f"⚠️ [{uuid}] No se pudo parsear fecha: '{raw_fecha}' (intento {attempts})")
                if attempts >= 2:
                    await session_manager.client.delete(BALANCE_CTX_KEY)
                    logger.info(f"🚨 [{uuid}] Fecha invalida {attempts} veces — redirigiendo a asesor directo")
                    return await self.invoke_redirect_to_agent_direct(
                        uuid, query, history_messages, session, platform_id, usuario
                    )
                balance_ctx["fecha_attempts"] = attempts
                await session_manager.client.setex(BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx))
                answer = "No pude interpretar la fecha. Por favor ingresa tu fecha de nacimiento, por ejemplo: 25 de julio de 1997 o 25-07-1997."
                history_messages.append(LCHumanMessage(content=query))
                history_messages.append(LCAIMessage(content=answer))
                session.state["messages"] = history_messages
                await session_manager.save_session(uuid, session)
                return SearchChatResponse(
                    uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
                )

            await session_manager.client.delete(BALANCE_CTX_KEY)
            await self.identity.save_identity(session_manager.client, uuid, nro_ide, fecha_nacimiento)
            logger.info(f"💰 [{uuid}] Fecha recibida: '{raw_fecha}' → parseada: {fecha_nacimiento}. Invocando consultar_deuda_actual.")

            answer = "No pude consultar tu saldo en este momento. Por favor intenta más tarde."
            try:
                tools = get_tools()
                saldo_tool = next((t for t in tools if t.name == "consultar_deuda_actual"), None)

                if saldo_tool:
                    logger.info(f"📡 [{uuid}] Invocando consultar_deuda_actual via MCP...")
                    result = saldo_tool.invoke({"nro_ide": nro_ide, "fecha_nacimiento": fecha_nacimiento})
                    logger.info(f"📥 [{uuid}] Resultado MCP saldo: {str(result)[:500]}")

                    balance_data = _extract_mcp_result(result)
                    balance_text = (
                        json_mod.dumps(balance_data, ensure_ascii=False, indent=2)
                        if isinstance(balance_data, dict)
                        else str(balance_data)
                    )

                    has_history = len(history_messages) > 0
                    history_hint = (
                        "IMPORTANTE: Ya hay conversación previa. NO saludes ni digas 'Hola'. Ve directo a la información.\n"
                        if has_history else ""
                    )
                    system_prompt = BALANCE_PROMPT.format(
                        history_hint=history_hint,
                        balance_data=balance_text,
                    )

                    recent_history = history_messages[-10:] if len(history_messages) > 10 else history_messages
                    llm_messages = [SystemMessage(content=system_prompt)]
                    llm_messages.extend(recent_history)
                    llm_messages.append(LCHumanMessage(content=f"Consulta de saldo para cédula {nro_ide}"))

                    response = _llm.invoke(llm_messages)
                    answer = response.content
                    logger.info(f"✅ [{uuid}] Respuesta de saldo generada con LLM")
                else:
                    logger.warning(f"⚠️ [{uuid}] Tool consultar_deuda_actual no encontrado en MCP")

            except Exception as e:
                logger.error(f"❌ [{uuid}] Error en consultar_deuda_actual: {e}")

            history_messages.append(LCHumanMessage(content=query))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            return SearchChatResponse(
                uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
            )

        return None

    async def init_balance_flow(
        self,
        uuid: str,
        question: str,
        history_messages: list,
        session,
        existing_identity: dict,
        platform_id: str | None = None,
        usuario: str | None = None,
    ) -> SearchChatResponse:
        """Inicializa el flujo BALANCE, reutilizando datos de identidad si están disponibles."""
        BALANCE_CTX_KEY = f"search_balance_ctx:{uuid}"
        saved_cedula = existing_identity.get("cedula")
        saved_fecha = existing_identity.get("fecha_nacimiento")

        if saved_cedula and saved_fecha:
            logger.info(f"🔑 [{uuid}] Reutilizando identidad existente: cédula={saved_cedula}")
            answer = "Ya tengo tus datos registrados. Consultando tu saldo."
            history_messages.append(LCHumanMessage(content=question))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            balance_ctx: dict = {"nro_ide": saved_cedula, "step": "awaiting_fecha"}
            if platform_id:
                balance_ctx["platform_id"] = platform_id
            if usuario:
                balance_ctx["usuario"] = usuario
            balance_response = await self.handle_balance_flow(uuid, balance_ctx, saved_fecha)
            if balance_response:
                return balance_response

        if saved_cedula:
            logger.info(f"🔑 [{uuid}] Reutilizando cédula existente: {saved_cedula}. Pidiendo fecha.")
            answer = "✅ Ya tengo tu cédula registrada.\n\nAhora solo necesito tu fecha de nacimiento 📅"
            balance_ctx = {"step": "awaiting_fecha", "nro_ide": saved_cedula}
            if platform_id:
                balance_ctx["platform_id"] = platform_id
            if usuario:
                balance_ctx["usuario"] = usuario
            await session_manager.client.setex(
                BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx)
            )
            history_messages.append(LCHumanMessage(content=question))
            history_messages.append(LCAIMessage(content=answer))
            session.state["messages"] = history_messages
            await session_manager.save_session(uuid, session)

            return SearchChatResponse(
                uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
            )

        answer = "Con gusto te ayudo a consultar tu saldo 😊\nPara poder verificar tu información, necesito tu número de cédula."
        balance_ctx = {"step": "awaiting_cedula"}
        if platform_id:
            balance_ctx["platform_id"] = platform_id
        if usuario:
            balance_ctx["usuario"] = usuario
        await session_manager.client.setex(
            BALANCE_CTX_KEY, BALANCE_CTX_TTL, json_mod.dumps(balance_ctx)
        )
        history_messages.append(LCHumanMessage(content=question))
        history_messages.append(LCAIMessage(content=answer))
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid, answer=answer, intent="BALANCE", total=0, products=[]
        )

    # -------------------------------------------------------------------------
    # FLUJO DE PAGO
    # -------------------------------------------------------------------------

    async def handle_payment(
        self,
        uuid: str,
        query: str,
        history_messages: list,
    ) -> SearchChatResponse:
        """Consulta las opciones de pago via MCP get_payment_options y genera respuesta con LLM."""
        logger.info(f"💳 [{uuid}] Consultando opciones de pago via MCP")

        tool_result = "No se pudieron obtener las opciones de pago en este momento."
        try:
            tools = get_tools()
            payment_tool = next((t for t in tools if t.name == "get_payment_options"), None)

            if payment_tool:
                logger.info(f"📡 [{uuid}] Invocando get_payment_options via MCP...")
                raw_result = payment_tool.invoke({})
                logger.info(f"📥 [{uuid}] Resultado MCP payment: {str(raw_result)[:500]}")

                payment_data = _extract_mcp_result(raw_result)
                if isinstance(payment_data, (dict, list)):
                    tool_result = json_mod.dumps(payment_data, ensure_ascii=False, indent=2)
                else:
                    tool_result = str(payment_data)
            else:
                logger.warning(f"⚠️ [{uuid}] Tool get_payment_options no encontrado en MCP")

        except Exception as e:
            logger.error(f"❌ [{uuid}] Error en get_payment_options: {e}")

        has_history = len(history_messages) > 0
        history_hint = (
            "IMPORTANTE: Ya hay conversación previa. NO saludes ni digas 'Hola'. Ve directo a la información de pago.\n"
            if has_history else ""
        )
        system_prompt = PAYMENT_PROMPT.format(history_hint=history_hint, tool_result=tool_result)

        llm_messages = [SystemMessage(content=system_prompt)]
        recent_history = history_messages[-10:] if len(history_messages) > 10 else history_messages
        llm_messages.extend(recent_history)
        llm_messages.append(LCHumanMessage(content=query))

        response = _llm.invoke(llm_messages)
        answer = response.content
        logger.info(f"✅ [{uuid}] Respuesta de pago generada")

        session = await session_manager.get_session(uuid)
        history_messages.append(LCHumanMessage(content=query))
        history_messages.append(LCAIMessage(content=answer))
        if len(history_messages) > MAX_TOTAL_HISTORY:
            history_messages = history_messages[-MAX_TOTAL_HISTORY:]
        session.state["messages"] = history_messages
        await session_manager.save_session(uuid, session)

        return SearchChatResponse(
            uuid_conversation=uuid, answer=answer, intent="PAYMENT", total=0, products=[]
        )
