"""Virtuals ACP private negotiation over Skytale channels.

Wraps ACP's NEGOTIATION phase in an MLS-encrypted Skytale channel,
so negotiation details (requirements, offers, counter-offers) are
never visible to third parties.

Usage::

    from skytale_sdk.integrations import acp_private

    bridge = acp_private.create_bridge(manager, acp_client)
    channel = bridge.start_negotiation(job_id="123")
    bridge.send_requirement(channel, {"min_quality": 0.9, "max_price": 100})
    offer = bridge.receive_offer(channel)

Requires ``@virtuals-protocol/acp-node`` (or Python equivalent) as a
peer dependency.
"""

from __future__ import annotations

import json
import time
from dataclasses import dataclass, field
from typing import Optional

from skytale_sdk.channels import SkytaleChannelManager


@dataclass
class NegotiationMessage:
    """A message in an ACP negotiation.

    Attributes:
        msg_type: Message type (requirement, offer, counter, accept, reject).
        content: Message content dict.
        sender: Sender identity.
        timestamp: Unix timestamp.
    """

    msg_type: str
    content: dict
    sender: str = ""
    timestamp: float = field(default_factory=time.time)


class AcpBridge:
    """Bridge between Virtuals ACP and Skytale encrypted channels.

    Creates per-job encrypted channels for ACP negotiation phases.
    The ACP contract handles on-chain escrow and settlement;
    Skytale handles off-chain encrypted negotiation.

    Args:
        manager: Skytale channel manager for encrypted communication.
        namespace: Channel namespace prefix (default: ``"acp/jobs"``).
    """

    def __init__(
        self,
        manager: SkytaleChannelManager,
        namespace: str = "acp/jobs",
    ):
        self._mgr = manager
        self._namespace = namespace

    def start_negotiation(self, job_id: str) -> str:
        """Create an encrypted channel for a negotiation.

        Args:
            job_id: ACP job ID.

        Returns:
            Channel name (e.g. ``"acp/jobs/123"``).
        """
        channel = f"{self._namespace}/{job_id}"
        self._mgr.create(channel)
        return channel

    def invite_counterparty(self, channel: str, max_uses: int = 1) -> str:
        """Generate an invite token for the negotiation counterparty.

        Args:
            channel: Negotiation channel name.
            max_uses: How many agents can join (default 1 for bilateral).

        Returns:
            Invite token string.
        """
        return self._mgr.invite(channel, max_uses=max_uses)

    def join_negotiation(self, channel: str, token: str) -> None:
        """Join an existing negotiation channel.

        Args:
            channel: Negotiation channel name.
            token: Invite token from the counterparty.
        """
        self._mgr.join_with_token(channel, token)

    def send_requirement(self, channel: str, requirements: dict) -> None:
        """Send requirements to the negotiation channel.

        Args:
            channel: Negotiation channel name.
            requirements: Dict of requirements (e.g. min_quality, max_price).
        """
        msg = json.dumps({
            "type": "requirement",
            "content": requirements,
            "timestamp": time.time(),
        })
        self._mgr.send(channel, msg)

    def send_offer(self, channel: str, offer: dict) -> None:
        """Send an offer to the negotiation channel.

        Args:
            channel: Negotiation channel name.
            offer: Dict of offer details (e.g. price, terms).
        """
        msg = json.dumps({
            "type": "offer",
            "content": offer,
            "timestamp": time.time(),
        })
        self._mgr.send(channel, msg)

    def send_counter(self, channel: str, counter: dict) -> None:
        """Send a counter-offer.

        Args:
            channel: Negotiation channel name.
            counter: Counter-offer details.
        """
        msg = json.dumps({
            "type": "counter",
            "content": counter,
            "timestamp": time.time(),
        })
        self._mgr.send(channel, msg)

    def accept(self, channel: str) -> None:
        """Accept the current offer.

        Args:
            channel: Negotiation channel name.
        """
        msg = json.dumps({"type": "accept", "timestamp": time.time()})
        self._mgr.send(channel, msg)

    def reject(self, channel: str, reason: str = "") -> None:
        """Reject the current offer.

        Args:
            channel: Negotiation channel name.
            reason: Optional rejection reason.
        """
        msg = json.dumps({
            "type": "reject",
            "reason": reason,
            "timestamp": time.time(),
        })
        self._mgr.send(channel, msg)

    def receive(self, channel: str, timeout: float = 10.0) -> list[NegotiationMessage]:
        """Receive negotiation messages.

        Args:
            channel: Negotiation channel name.
            timeout: Seconds to wait.

        Returns:
            List of parsed negotiation messages.
        """
        raw_msgs = self._mgr.receive(channel, timeout=timeout)
        messages = []
        for raw in raw_msgs:
            try:
                data = json.loads(raw)
                messages.append(NegotiationMessage(
                    msg_type=data.get("type", "unknown"),
                    content=data.get("content", {}),
                    sender=data.get("sender", ""),
                    timestamp=data.get("timestamp", 0),
                ))
            except (json.JSONDecodeError, TypeError):
                continue
        return messages


def create_bridge(
    manager: SkytaleChannelManager,
    namespace: str = "acp/jobs",
) -> AcpBridge:
    """Create an ACP negotiation bridge.

    Args:
        manager: Skytale channel manager.
        namespace: Channel namespace prefix.

    Returns:
        Configured :class:`AcpBridge`.
    """
    return AcpBridge(manager, namespace)
