"""ACP (Agent Communication Protocol) adapter — **DEPRECATED**.

ACP has been superseded by Google's A2A (Agent-to-Agent) protocol.  This module
preserves the original ACP wire behavior (``org/acp/`` namespace,
``Protocol.ACP`` envelope tags, plain-dict payloads) so existing agents continue
to interoperate without requiring simultaneous upgrades.

Emits :class:`DeprecationWarning` at construction time.

Existing code that imports from this module continues to work unchanged:

.. code-block:: python

    # Still works, emits DeprecationWarning:
    from skytale_sdk.integrations._acp import SkytaleACPAdapter

    # Preferred — use the A2A adapter directly:
    from skytale_sdk.integrations._a2a import SkytaleA2AAdapter

Migration guide:

=============================  =============================
ACP (deprecated)               A2A (replacement)
=============================  =============================
``SkytaleACPAdapter``          ``SkytaleA2AAdapter``
``create_task(task_id)``       ``create_context(context_id)``
``join_task(task_id, …)``      ``join_context(context_id, …)``
``send_message(task_id, …)``   ``send_message(context_id, …)``
=============================  =============================

.. warning::

    The A2A adapter uses a **different wire format** (JSON-RPC 2.0 envelopes,
    ``org/a2a/`` namespace, ``Protocol.A2A`` tags).  Migrating from ACP to A2A
    requires all participants in a channel to upgrade simultaneously.
"""

from __future__ import annotations

import json
import logging
import uuid
import warnings
from typing import Dict, List

from skytale_sdk.channels import SkytaleChannelManager
from skytale_sdk.envelope import Envelope, Protocol

logger = logging.getLogger(__name__)


class SkytaleACPAdapter:
    """Deprecated ACP adapter — preserves original ACP wire behavior.

    Each ACP task maps to a Skytale channel at ``org/acp/{task_id}``.
    Messages are serialized as plain JSON dicts, wrapped in an
    :class:`Envelope` with ``Protocol.ACP``, and sent through
    MLS-encrypted channels.

    Emits :class:`DeprecationWarning` at construction time.

    .. deprecated::
        Use :class:`~skytale_sdk.integrations._a2a.SkytaleA2AAdapter`.

    Args:
        manager: A :class:`SkytaleChannelManager` that owns the underlying
            encrypted channels.
        agent_id: Identifier for this agent, embedded in every outgoing ACP
            message so receivers know who sent it.

    Example::

        # Deprecated — still works:
        from skytale_sdk.integrations._acp import SkytaleACPAdapter
        adapter = SkytaleACPAdapter(mgr, agent_id="agent-42")

        # Preferred:
        from skytale_sdk.integrations._a2a import SkytaleA2AAdapter
        adapter = SkytaleA2AAdapter(mgr, agent_id="agent-42")
    """

    def __init__(self, manager: SkytaleChannelManager, agent_id: str) -> None:
        warnings.warn(
            "SkytaleACPAdapter is deprecated, use SkytaleA2AAdapter "
            "from skytale_sdk.integrations._a2a instead. "
            "Note: migrating to A2A changes the wire format — all "
            "participants in a channel must upgrade simultaneously.",
            DeprecationWarning,
            stacklevel=2,
        )
        self._manager = manager
        self._agent_id = agent_id

    # ------------------------------------------------------------------
    # Internal helpers
    # ------------------------------------------------------------------

    def _channel_name(self, task_id: str) -> str:
        """Convert an ACP task ID to a Skytale channel name.

        ACP tasks live under the ``org/acp/`` namespace so they do not
        collide with channels created by other protocols or directly by
        users.

        Args:
            task_id: The ACP task identifier.

        Returns:
            A Skytale channel name in ``org/acp/{task_id}`` format.

        Example::

            >>> adapter._channel_name("analysis-42")
            'org/acp/analysis-42'
        """
        return f"org/acp/{task_id}"

    # ------------------------------------------------------------------
    # Task lifecycle
    # ------------------------------------------------------------------

    def create_task(self, task_id: str) -> None:
        """Create a new ACP task backed by a Skytale encrypted channel.

        The underlying MLS group is created immediately and a background
        listener thread starts buffering incoming messages.

        .. deprecated::
            Use :meth:`SkytaleA2AAdapter.create_context`.

        Args:
            task_id: Unique identifier for the ACP task.

        Raises:
            RuntimeError: If channel creation fails (e.g. invalid name or
                relay unreachable).

        Example::

            adapter.create_task("analysis-42")
        """
        channel = self._channel_name(task_id)
        self._manager.create(channel)
        logger.info("created ACP task %s on channel %s", task_id, channel)

    def join_task(self, task_id: str, welcome: bytes) -> None:
        """Join an existing ACP task using an MLS Welcome message.

        Another agent must have already created the task and generated
        the Welcome via :meth:`SkytaleChannelManager.add_member`.

        .. deprecated::
            Use :meth:`SkytaleA2AAdapter.join_context`.

        Args:
            task_id: The ACP task identifier to join.
            welcome: MLS Welcome message bytes from the task creator.

        Raises:
            RuntimeError: If the Welcome is invalid or joining fails.

        Example::

            # On the joining agent, after receiving *welcome* out-of-band:
            adapter.join_task("analysis-42", welcome_bytes)
        """
        channel = self._channel_name(task_id)
        self._manager.join(channel, welcome)
        logger.info("joined ACP task %s on channel %s", task_id, channel)

    # ------------------------------------------------------------------
    # Messaging
    # ------------------------------------------------------------------

    def send_message(self, task_id: str, payload: Dict) -> None:
        """Send an ACP message with the given payload.

        Builds a JSON object conforming to the ACP message shape, wraps
        it in a :class:`~skytale_sdk.envelope.Envelope` with ``Protocol.ACP``,
        and sends it over the MLS-encrypted channel.

        The message is assigned a random ``messageId`` (UUID4).  The
        ``agentId`` and ``taskId`` fields are populated automatically.

        .. deprecated::
            Use :meth:`SkytaleA2AAdapter.send_message`.

        Args:
            task_id: ACP task to send the message on.
            payload: Arbitrary dict payload for the ACP message.

        Raises:
            KeyError: If the task has not been created or joined.

        Example::

            adapter.send_message("analysis-42", {
                "status": "complete",
                "result": "3 anomalies detected",
            })
        """
        message_id = str(uuid.uuid4())
        acp_message: Dict = {
            "messageId": message_id,
            "taskId": task_id,
            "agentId": self._agent_id,
            "payload": payload,
        }

        raw = json.dumps(acp_message, separators=(",", ":")).encode("utf-8")

        envelope = Envelope(
            protocol=Protocol.ACP,
            content_type="application/json",
            payload=raw,
            metadata={"task_id": task_id},
        )

        channel = self._channel_name(task_id)
        self._manager.send_envelope(channel, envelope)
        logger.debug(
            "sent ACP message %s on task %s (%d bytes)",
            message_id,
            task_id,
            len(raw),
        )

    def receive_messages(
        self, task_id: str, timeout: float = 5.0
    ) -> List[Dict]:
        """Receive ACP messages from a task.

        Drains all buffered envelopes from the channel, filters for
        ``Protocol.ACP``, deserializes their JSON payloads, and returns
        them as plain dicts.

        Non-ACP envelopes (e.g. raw strings or A2A messages on a bridged
        channel) are silently skipped.  Envelopes whose payloads are not
        valid JSON are logged as warnings and skipped.

        .. deprecated::
            Use :meth:`SkytaleA2AAdapter.receive_messages`.

        Args:
            task_id: ACP task to read from.
            timeout: Maximum seconds to wait for messages.  ``0`` returns
                immediately.

        Returns:
            List of ACP message dicts.  Empty if no ACP messages arrive
            before the timeout expires.

        Raises:
            KeyError: If the task has not been created or joined.
            RuntimeError: If the background listener thread has died.

        Example::

            msgs = adapter.receive_messages("analysis-42", timeout=2.0)
            for msg in msgs:
                print(msg["payload"])
        """
        channel = self._channel_name(task_id)
        envelopes = self._manager.receive_envelopes(channel, timeout=timeout)

        messages: List[Dict] = []
        for env in envelopes:
            if env.protocol is not Protocol.ACP:
                continue
            try:
                msg = json.loads(env.payload)
            except (json.JSONDecodeError, UnicodeDecodeError) as exc:
                logger.warning(
                    "skipping ACP envelope with invalid JSON payload: %s", exc
                )
                continue
            messages.append(msg)

        logger.debug(
            "received %d ACP message(s) on task %s",
            len(messages),
            task_id,
        )
        return messages
