import json
import logging

from spade.agent import Message
from spade.behaviour import CyclicBehaviour

from control_backend.core.config import settings

logger = logging.getLogger(__name__)


class ContinuousBeliefCollector(CyclicBehaviour):
    """
    Continuously collects beliefs/emotions from extractor agents:
    Then we send a unified belief packet to the BDI agent.
    """

    async def run(self):
        msg = await self.receive(timeout=0.1)  # Wait for 0.1s
        if msg:
            await self._process_message(msg)

    async def _process_message(self, msg: Message):
        sender_node = self._sender_node(msg)

        # Parse JSON payload
        try:
            payload = json.loads(msg.body)
        except Exception as e:
            logger.warning(
                "BeliefCollector: failed to parse JSON from %s. Body=%r Error=%s",
                sender_node,
                msg.body,
                e,
            )
            return

        msg_type = payload.get("type")

        # Prefer explicit 'type' field
        if msg_type == "belief_extraction_text" or sender_node == "belief_text_agent_mock":
            logger.info(
                "BeliefCollector: message routed to _handle_belief_text (sender=%s)", sender_node
            )
            await self._handle_belief_text(payload, sender_node)
        # This is not implemented yet, but we keep the structure for future use
        elif msg_type == "emotion_extraction_text" or sender_node == "emo_text_agent_mock":
            logger.info(
                "BeliefCollector: message routed to _handle_emo_text (sender=%s)", sender_node
            )
            await self._handle_emo_text(payload, sender_node)
        else:
            logger.info(
                "BeliefCollector: unrecognized message (sender=%s, type=%r). Ignoring.",
                sender_node,
                msg_type,
            )

    @staticmethod
    def _sender_node(msg: Message) -> str:
        """
        Extracts the 'node' (localpart) of the sender JID.
        E.g., 'agent@host/resource' -> 'agent'
        """
        s = str(msg.sender) if msg.sender is not None else "no_sender"
        return s.split("@", 1)[0] if "@" in s else s

    async def _handle_belief_text(self, payload: dict, origin: str):
        """
        Expected payload:
         {
            "type": "belief_extraction_text",
            "beliefs": {"user_said": ["Can you help me?"]}

        }

        """
        beliefs = payload.get("beliefs", {})

        if not beliefs:
            logger.info("BeliefCollector: no beliefs to process.")
            return

        if not isinstance(beliefs, dict):
            logger.warning("BeliefCollector: 'beliefs' is not a dict: %r", beliefs)
            return

        if not all(isinstance(v, list) for v in beliefs.values()):
            logger.warning("BeliefCollector: 'beliefs' values are not all lists: %r", beliefs)
            return

        logger.info("BeliefCollector: forwarding %d beliefs.", len(beliefs))
        for belief_name, belief_list in beliefs.items():
            for belief in belief_list:
                logger.info("  - %s %s", belief_name, str(belief))

        await self._send_beliefs_to_bdi(beliefs, origin=origin)

    async def _handle_emo_text(self, payload: dict, origin: str):
        """TODO: implement (after we have emotional recogntion)"""
        pass

    async def _send_beliefs_to_bdi(self, beliefs: list[str], origin: str | None = None):
        """
        Sends a unified belief packet to the BDI agent.
        """
        if not beliefs:
            return

        to_jid = f"{settings.agent_settings.bdi_core_agent_name}@{settings.agent_settings.host}"

        msg = Message(to=to_jid, sender=self.agent.jid, thread="beliefs")
        msg.body = json.dumps(beliefs)

        await self.send(msg)
        logger.info("BeliefCollector: sent %d belief(s) to BDI at %s", len(beliefs), to_jid)
