from app.agents.prompting import apply_agent_profile_prompt
from app.agents.contas_prompting import load_domain_prompt
from app.agents.runtime import AgentRuntimeMixin
import re
import unicodedata


class FaturasAgent(AgentRuntimeMixin):
    name = "faturas_agent"

    def __init__(
        self,
        llm,
        telemetry=None,
        tool_router=None,
        rag_service=None,
        cache=None,
        settings=None,
        observer=None,
        memory=None,
        summary_memory=None,
        guardrail_pipeline=None,
    ):
        self.llm = llm
        self.telemetry = telemetry
        self.tool_router = tool_router
        self.rag_service = rag_service
        self.cache = cache
        self.settings = settings
        self.observer = observer
        self.memory = memory
        self.summary_memory = summary_memory
        self.guardrail_pipeline = guardrail_pipeline


    @staticmethod
    def _normalize_text(value):
        text = unicodedata.normalize("NFKD", str(value or ""))
        return "".join(ch for ch in text if not unicodedata.combining(ch)).lower().strip()

    @classmethod
    def _is_live_internet_balance_request(cls, state):
        text = cls._normalize_text(
            state.get("sanitized_input") or state.get("user_text") or state.get("input") or ""
        )
        if not text:
            return False

        internet = bool(re.search(r"\b(internet|dados moveis|dados|franquia)\b", text))
        live_balance = bool(
            re.search(
                r"\b(quanto.*(?:resta|restou|tenho|sobrou|disponivel)|"
                r"saldo|restante|restam|ainda tenho|ainda posso usar|consumo atual)\b",
                text,
            )
        )
        return internet and live_balance

    @classmethod
    def _has_live_internet_balance_evidence(cls, tool_context):
        # Não bloqueia uma integração futura que passe a devolver o saldo em tempo real.
        evidence_keys = {
            "saldo_internet",
            "internet_balance",
            "remaining_data",
            "remaining_data_mb",
            "remaining_data_gb",
            "saldo_dados",
            "franquia_disponivel",
            "consumo_atual",
            "current_usage",
        }

        def walk(value):
            if isinstance(value, dict):
                for key, nested in value.items():
                    if cls._normalize_text(key).replace(" ", "_") in evidence_keys and nested not in (None, "", [], {}):
                        return True
                    if walk(nested):
                        return True
            elif isinstance(value, list):
                return any(walk(item) for item in value)
            return False

        for item in tool_context or []:
            if not isinstance(item, dict) or item.get("ok") is False:
                continue
            if walk(item.get("result")):
                return True
        return False

    @classmethod
    def _live_internet_balance_guidance(cls, state, tool_context):
        if not cls._is_live_internet_balance_request(state):
            return None
        if cls._has_live_internet_balance_evidence(tool_context):
            return None
        return (
            "Não consigo consultar o saldo de internet em tempo real por aqui. "
            "Para ver quanto ainda resta neste mês, consulte o app Meu TIM, "
            "onde você acompanha o consumo atual da sua franquia."
        )

    @staticmethod
    def _handoff_patch_from_tool_context(tool_context):
        for item in tool_context or []:
            if not isinstance(item, dict):
                continue
            data = item.get("result")
            if not isinstance(data, dict):
                continue
            nested = data.get("result")
            if isinstance(nested, dict) and nested.get("session_control"):
                data = nested
            if str(data.get("session_control") or "").upper() != "HUMAN_HANDOFF":
                continue
            return {
                "session_control": "HUMAN_HANDOFF",
                "human_handoff_requested": True,
                "session_ended": True,
                "terminal_status": str(data.get("terminal_status") or "human_handoff"),
                "handoff_reason": str(data.get("handoff_reason") or ""),
            }
        return {}

    async def run(self, state):
        await self._emit_ic(
            "IC.FATURAS_AGENT_STARTED",
            state,
            {"business_component": "faturas"},
            component="agent.faturas.start",
        )

        tool_context = await self._collect_tool_context(state)
        if tool_context:
            await self._emit_ic(
                "IC.FATURAS_MCP_CONTEXT_COLLECTED",
                state,
                {"tool_result_count": len(tool_context)},
                component="agent.faturas.mcp",
            )

        state["mcp_results"] = tool_context
        handoff_patch = self._handoff_patch_from_tool_context(tool_context)
        clarification_message = self.transaction_clarification_message(state)
        if clarification_message:
            return {
                "answer": f"[{self.__class__.__name__}] {clarification_message}",
                "next_state": state.get("next_state") or "COLLECTING_PARAMETERS",
                "mcp_results": tool_context,
                **self.transaction_state_patch(state),
                **handoff_patch,
            }

        confirmation_message = self.transaction_confirmation_message(state)
        if confirmation_message:
            result = {
                "answer": f"[{self.__class__.__name__}] {confirmation_message}",
                "next_state": state.get("next_state"),
                "mcp_results": tool_context,
                **self.transaction_state_patch(state),
                **handoff_patch,
            }
            return result

        live_balance_guidance = self._live_internet_balance_guidance(state, tool_context)
        if live_balance_guidance:
            return {
                "answer": f"[FaturasAgent] {live_balance_guidance}",
                "next_state": state.get("next_state") or "ACTIVE",
                "mcp_results": tool_context,
                "rag": {"enabled": False, "skipped": True, "reason": "live_internet_balance_not_available"},
                **self.transaction_state_patch(state),
                **handoff_patch,
            }

        direct_answer = self.build_direct_mcp_answer(state, tool_context, agent_label="FaturasAgent")
        if direct_answer:
            return {
                "answer": direct_answer,
                "next_state": state.get("next_state") or "ACTIVE",
                "mcp_results": tool_context,
                "rag": {"enabled": False, "skipped": True, "reason": "direct_mcp_answer"},
                **self.transaction_state_patch(state),
                **handoff_patch,
            }

        rag_context, rag_metadata = await self._retrieve_rag_context(state)
        if rag_metadata.get("enabled"):
            await self._emit_ic(
                "IC.FATURAS_RAG_CONTEXT_RETRIEVED",
                state,
                {
                    "document_count": rag_metadata.get("document_count"),
                    "graph_neighbors": rag_metadata.get("graph_neighbors"),
                    "latency_ms": rag_metadata.get("latency_ms"),
                },
                component="agent.faturas.rag",
            )

        # Prepara ConversationSummaryMemory antes de montar o prompt.
        # O build_messages() do framework injeta resumo + últimas mensagens quando habilitado.
        await self.prepare_memory_context(state)

        messages = self.build_messages(
            state,
            system_prompt=apply_agent_profile_prompt(
                state,
                load_domain_prompt("billing"),
            ),
            mcp_results=tool_context,
            rag_context=rag_context,
            rag_metadata=rag_metadata,
        )

        answer = await self._invoke_llm_cached(state, "FaturasAgent", messages)
        result = {
            "answer": f"[FaturasAgent] {answer}",
            "next_state": "FATURAS_ACTIVE",
            "mcp_results": tool_context,
            "rag": rag_metadata,
            "memory_context_metadata": state.get("memory_context_metadata"),
            **self.transaction_state_patch(state),
            **handoff_patch,
        }

        await self._emit_ic(
            "IC.FATURAS_AGENT_COMPLETED",
            state,
            {
                "answer_chars": len(result.get("answer") or ""),
                "has_mcp_results": bool(tool_context),
                "rag_enabled": bool(rag_metadata.get("enabled")),
                "memory_context": state.get("memory_context_metadata"),
            },
            component="agent.faturas.completed",
        )
        return result

    async def _collect_tool_context(self, state):
        return await self._collect_mcp_context(state)
