"""coffeeAGNTCY on Skytale: encrypted agent-to-agent messaging demo.

coffeeAGNTCY is a multi-agent reference app from AGNTCY where a Coffee
Exchange agent negotiates prices with a Coffee Farm agent.  This example
shows the same pattern over Skytale's encrypted MLS channels using the
Python SDK and A2A protocol envelopes.

Two async agents run in the same process:

  Exchange -- creates a channel, generates an invite token
  Farm     -- joins with the token, starts negotiating

Messages use ``Envelope`` with ``Protocol.A2A`` so they're compatible
with A2A tooling while being MLS-encrypted end-to-end.

Usage:
    # Local dev mode (no relay or API key required):
    python coffeeagntcy_skytale.py --mock

    # Against the hosted relay:
    skytale signup you@example.com
    python coffeeagntcy_skytale.py

    # Custom endpoint:
    python coffeeagntcy_skytale.py --api-key sk_live_... --endpoint https://relay.skytale.sh:5000
"""

import argparse
import asyncio
import json
import time

from skytale_sdk import SkytaleChannelManager, Envelope, Protocol

CHANNEL = "coffeeagntcy/trading/negotiation"


def make_a2a_message(sender: str, text: str) -> Envelope:
    """Wrap a text message in an A2A protocol envelope."""
    payload = json.dumps({
        "parts": [{"type": "text", "text": text}],
        "sender": sender,
    }).encode("utf-8")
    return Envelope(
        protocol=Protocol.A2A,
        content_type="application/json",
        payload=payload,
    )


def read_a2a_text(envelope: Envelope) -> str:
    """Extract the text from an A2A envelope."""
    body = json.loads(envelope.payload)
    parts = body.get("parts", [])
    return parts[0]["text"] if parts else ""


async def exchange_agent(
    api_key: str | None,
    endpoint: str | None,
    mock: bool,
) -> str:
    """Exchange agent: creates a channel and negotiates a price.

    Returns the invite token so the Farm agent can join.
    """
    mgr = SkytaleChannelManager(
        identity="exchange",
        api_key=api_key,
        endpoint=endpoint,
        mock=mock,
    )
    mgr.create(CHANNEL)
    print("[Exchange] Created channel:", CHANNEL)

    token = mgr.invite(CHANNEL) if not mock else "mock_token"
    print("[Exchange] Invite token generated")

    # Wait for Farm to join, then negotiate.
    time.sleep(2)  # give Farm time to connect

    mgr.send_envelope(CHANNEL, make_a2a_message(
        "exchange", "Requesting quote: 100 bags Arabica, delivery Q3 2026"
    ))
    print("[Exchange] Sent quote request")

    # Wait for Farm's response.
    envelopes = mgr.receive_envelopes(CHANNEL, timeout=10.0)
    for env in envelopes:
        text = read_a2a_text(env)
        print(f"[Exchange] Received: {text}")

    mgr.send_envelope(CHANNEL, make_a2a_message(
        "exchange", "Accepted. Locking price at $4.20/lb for 100 bags."
    ))
    print("[Exchange] Deal confirmed")

    mgr.close()
    return token


async def farm_agent(
    token: str,
    api_key: str | None,
    endpoint: str | None,
    mock: bool,
) -> None:
    """Farm agent: joins via invite token and responds to the negotiation."""
    mgr = SkytaleChannelManager(
        identity="farm",
        api_key=api_key,
        endpoint=endpoint,
        mock=mock,
    )

    if mock:
        # In mock mode, create + join locally (no API for token exchange).
        mgr.create(CHANNEL)
    else:
        mgr.join_with_token(CHANNEL, token)
    print("[Farm] Joined channel:", CHANNEL)

    # Wait for Exchange's quote request.
    envelopes = mgr.receive_envelopes(CHANNEL, timeout=10.0)
    for env in envelopes:
        text = read_a2a_text(env)
        print(f"[Farm] Received: {text}")

    mgr.send_envelope(CHANNEL, make_a2a_message(
        "farm", "Quote: $4.20/lb Arabica, 100 bags, FOB Santos. Valid 24h."
    ))
    print("[Farm] Sent quote")

    # Wait for confirmation.
    envelopes = mgr.receive_envelopes(CHANNEL, timeout=10.0)
    for env in envelopes:
        text = read_a2a_text(env)
        print(f"[Farm] Received: {text}")

    mgr.close()


async def main() -> None:
    parser = argparse.ArgumentParser(
        description="coffeeAGNTCY on Skytale: encrypted agent negotiation demo"
    )
    parser.add_argument(
        "--mock",
        action="store_true",
        help="Run in local dev mode (no relay or API key required)",
    )
    parser.add_argument(
        "--api-key",
        default=None,
        help="Skytale API key (default: reads from ~/.skytale/api-key)",
    )
    parser.add_argument(
        "--endpoint",
        default=None,
        help="Relay endpoint URL (default: https://relay.skytale.sh:5000)",
    )
    args = parser.parse_args()

    print("=== coffeeAGNTCY on Skytale ===")
    print(f"Mode: {'mock (local)' if args.mock else 'live'}")
    print()

    # In mock mode, run agents sequentially since they share in-memory state.
    # In live mode, the invite token flow handles coordination.
    if args.mock:
        token = await exchange_agent(args.api_key, args.endpoint, mock=True)
        await farm_agent(token, args.api_key, args.endpoint, mock=True)
    else:
        token = await exchange_agent(args.api_key, args.endpoint, mock=False)
        await farm_agent(token, args.api_key, args.endpoint, mock=False)

    print()
    print("=== Negotiation complete ===")


if __name__ == "__main__":
    asyncio.run(main())
