"""Protocol adapter for inbound/outbound message normalization."""

from __future__ import annotations

import json
import time
import uuid
from typing import Callable, Optional, Any, Dict, Tuple


class ProtocolAdapter:
    """Handles multi-protocol decoding and receipt formatting."""

    FIPA_PERFORMATIVES = {
        "accept-proposal", "agree", "cancel", "cfp", "confirm", "disconfirm",
        "failure", "inform", "inform-if", "inform-ref", "not-understood",
        "propose", "query-if", "query-ref", "refuse", "reject-proposal",
        "request", "request-when", "request-whenever", "subscribe", "proxy",
        "propagate", "inform-done", "inform-result", "request-whenever",
        "request-when", "query-if", "query-ref",
    }

    KQML_VERBS = {
        "ask-if", "ask-all", "ask-one", "tell", "achieve", "advertise",
        "subscribe", "reply", "sorry", "error", "deny", "unachieve",
        "insert", "uninsert", "assert", "unassert", "stream-all", "stream-one",
        "eos", "next", "rest",
    }

    def __init__(self, send_task: Callable[..., Any], strict_mode: bool = False):
        self._send_task = send_task
        self.strict_mode = strict_mode

    def _validate_fipa(self, message: Dict[str, Any]) -> Tuple[bool, str]:
        required = ["performative", "sender", "receiver", "content"]
        for field in required:
            if field not in message:
                return False, f"Missing FIPA field: {field}"
        performative = str(message.get("performative", "")).lower()
        if performative not in self.FIPA_PERFORMATIVES:
            return False, f"Invalid FIPA performative: {performative}"
        return True, "OK"

    def _validate_kqml(self, message: Dict[str, Any]) -> Tuple[bool, str]:
        required = ["verb", "content"]
        for field in required:
            if field not in message:
                return False, f"Missing KQML field: {field}"
        verb = str(message.get("verb", "")).lower()
        if verb not in self.KQML_VERBS:
            return False, f"Invalid KQML verb: {verb}"
        return True, "OK"

    def _validate_jsonrpc(self, message: Dict[str, Any]) -> Tuple[bool, str]:
        if message.get("jsonrpc") != "2.0":
            return False, "Invalid JSON-RPC version"
        if "method" not in message or "id" not in message:
            return False, "Missing JSON-RPC method or id"
        return True, "OK"

    def receive_message(
        self,
        message: Any,
        priority: int = 2,
        complexity: int = 5,
        ttl: float = 10,
        return_ack: bool = False,
        backpressure: bool = False,
        max_backpressure_delay: float = 0.5,
    ):
        """Decode multi-protocol messages and optionally return an ack.

        Returns:
            Task ID in queue, or (task_id, ack) if return_ack is True.
        """
        protocol = "SIMPLE"
        m_id = str(uuid.uuid4())[:8]
        task = str(message)

        if isinstance(message, dict):
            # FIPA ACL
            if "performative" in message:
                protocol = "FIPA"
                if self.strict_mode:
                    valid, reason = self._validate_fipa(message)
                    if not valid:
                        if return_ack:
                            return None, {
                                "status": "REJECTED",
                                "task_id": None,
                                "retry_after": 0.0,
                                "reason": reason,
                            }
                        return None
                m_id = message.get("conversation-id", m_id)
                task = f"[{message['performative'].upper()}] {message['content']}"
            # KQML
            elif "verb" in message:
                protocol = "KQML"
                if self.strict_mode:
                    valid, reason = self._validate_kqml(message)
                    if not valid:
                        if return_ack:
                            return None, {
                                "status": "REJECTED",
                                "task_id": None,
                                "retry_after": 0.0,
                                "reason": reason,
                            }
                        return None
                m_id = message.get("reply-with", m_id)
                task = f"[{message['verb'].upper()}] {message['content']}"
            # JSON-RPC
            elif "jsonrpc" in message:
                protocol = "JSON-RPC"
                if self.strict_mode:
                    valid, reason = self._validate_jsonrpc(message)
                    if not valid:
                        if return_ack:
                            return None, {
                                "status": "REJECTED",
                                "task_id": None,
                                "retry_after": 0.0,
                                "reason": reason,
                            }
                        return None
                m_id = str(message.get("id", m_id))
                task = f"[METHOD:{message.get('method')}] {json.dumps(message.get('params', {}))}"

        result = self._send_task(
            task,
            priority,
            complexity,
            ttl,
            m_id,
            protocol,
            backpressure=backpressure,
            max_backpressure_delay=max_backpressure_delay,
            return_metadata=return_ack,
        )

        if not return_ack:
            return result

        ack = {
            "status": "ACCEPTED" if result and result.get("accepted") else "REJECTED",
            "task_id": result.get("task_id") if result else None,
            "retry_after": result.get("retry_after", 0.0) if result else 0.0,
            "reason": result.get("reason") if result else "UNKNOWN",
        }
        return result.get("task_id") if result else None, ack

    def create_receipt(
        self,
        m_id: str,
        status: str,
        result: Any,
        t_arrival: float,
        t_start: float,
        proto: str,
        correlation_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Encapsulates response in original protocol with correlation ID."""
        core: Dict[str, Any] = {
            "mission_id": m_id,
            "status": status,
            "result": result,
            "wait": round(t_start - t_arrival, 4),
            "execution": round(time.time() - t_start, 4),
        }
        if correlation_id:
            core["correlation_id"] = correlation_id
        if proto == "FIPA":
            return {"performative": "inform", "content": core, "conversation-id": m_id}
        if proto == "KQML":
            return {"verb": "tell", "content": core, "reply-with": m_id}
        if proto == "JSON-RPC":
            return {"jsonrpc": "2.0", "result": core, "id": m_id}
        return core
