"""CAP Protocol — Cross-agent query and notification logic.

Handles the actual communication between SOAGILA instances:
- Outbound: query peers, broadcast notifications
- Inbound: handle cross-agent recalls and notifications
- Receipt chain: create linked audit receipts for cross-agent operations
"""

import json
import logging
from datetime import datetime, timezone

from soagila.core.cap.discovery import (
    get_current_agent_id,
    get_current_auth_token,
    get_current_product_type,
    get_current_project_hash,
    get_peer_agents,
)
from soagila.core.cap.registry import verify_peer

logger = logging.getLogger("soagila.cap.protocol")


async def _http_post(url: str, body: dict, timeout: float = 5.0) -> dict:
    """Send an HTTP POST to a peer agent. Returns parsed JSON response."""
    try:
        import httpx
        async with httpx.AsyncClient(timeout=timeout) as client:
            resp = await client.post(url, json=body)
            data = resp.json()
            return data.get("data", data) if data.get("success") else {}
    except ImportError:
        # httpx not installed — fallback to urllib (sync, less ideal)
        import urllib.request
        req = urllib.request.Request(
            url,
            data=json.dumps(body).encode(),
            headers={"Content-Type": "application/json"},
        )
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                data = json.loads(resp.read())
                return data.get("data", data) if data.get("success") else {}
        except Exception:
            return {}
    except Exception as e:
        logger.debug(f"HTTP POST to {url} failed: {e}")
        return {}


async def query_peer(
    peer: dict,
    query: str,
    tiers: list[str] | None = None,
    max_results: int = 5,
) -> dict:
    """Send a recall query to a single peer agent via HTTP."""
    agent_id = get_current_agent_id()
    auth_token = get_current_auth_token()
    if not agent_id or not auth_token:
        return {"error": "Agent not registered"}

    port = peer.get("port", 0)
    if not port:
        return {"error": "Peer has no HTTP port"}

    url = f"http://127.0.0.1:{port}/agent/recall"
    result = await _http_post(url, {
        "query": query,
        "tiers": tiers or ["episodic", "semantic"],
        "max_results": max_results,
        "source_agent_id": agent_id,
        "auth_token": auth_token,
    })

    if result:
        result["source_product"] = peer.get("product_type", "unknown")
        result["source_agent_id"] = peer.get("agent_id", "unknown")

    return result


async def query_all_peers(
    query: str,
    tiers: list[str] | None = None,
    max_results: int = 5,
    target_product: str | None = None,
) -> list[dict]:
    """Query all peer agents for the same project. Returns aggregated results."""
    peers = await get_peer_agents(same_project_only=True)

    if target_product and target_product != "all":
        peers = [p for p in peers if p.get("product_type") == target_product]

    results = []
    for peer in peers:
        try:
            peer_result = await query_peer(peer, query, tiers, max_results)
            if peer_result and "error" not in peer_result:
                results.append(peer_result)
        except Exception as e:
            logger.warning(f"Peer query failed for {peer.get('agent_id')}: {e}")

    return results


async def handle_cross_recall(body: dict) -> dict:
    """Handle an incoming cross-agent recall request.

    Verifies authentication, executes local recall, adds provenance,
    and creates an audit receipt.
    """
    # 1. Verify auth
    source_agent = body.get("source_agent_id", "")
    token = body.get("auth_token", "")
    if not await verify_peer(source_agent, token):
        raise ValueError("Authentication failed: invalid agent credentials")

    # 2. Execute local recall
    from soagila.tools.recall import handle as recall_handle
    result_content = await recall_handle({
        "query": body.get("query", ""),
        "tiers": body.get("tiers", ["episodic", "semantic"]),
        "max_results": body.get("max_results", 5),
    })

    result = json.loads(result_content[0].text)

    # 3. Add provenance
    result["provenance"] = {
        "source_product": get_current_product_type(),
        "source_agent_id": get_current_agent_id(),
        "served_at": datetime.now(timezone.utc).isoformat(),
    }

    # 4. Create cross-agent receipt
    try:
        from soagila.core.idl.ledger import create_receipt
        import hashlib

        receipt = await create_receipt(
            receipt_type="cross_agent_query",
            metadata={
                "querying_agent": source_agent,
                "serving_agent": get_current_agent_id(),
                "query": body.get("query", "")[:100],
                "results_count": result.get("total_results", 0),
            },
            context_hash=hashlib.sha256(
                body.get("query", "").encode()
            ).hexdigest()[:16],
            source_agent_id=source_agent,
            target_agent_id=get_current_agent_id(),
        )
        result["receipt_id"] = receipt.id
    except Exception as e:
        logger.warning(f"Failed to create cross-agent receipt: {e}")

    return result


async def handle_notification(body: dict) -> dict:
    """Handle an incoming notification from a peer agent."""
    # Verify auth
    source_agent = body.get("source_agent_id", "")
    token = body.get("auth_token", "")
    if not await verify_peer(source_agent, token):
        raise ValueError("Authentication failed: invalid agent credentials")

    # Publish to local event bus
    from soagila.db.event_bus import publish_event
    await publish_event("soagila:cross_agent", json.dumps({
        "type": "peer_notification",
        "from_agent": source_agent,
        "from_product": body.get("from_product", "unknown"),
        "notification_type": body.get("notification_type", "unknown"),
        "data": body.get("data", {}),
        "received_at": datetime.now(timezone.utc).isoformat(),
    }))

    return {"received": True}


async def notify_peers(
    notification_type: str,
    data: dict,
    project_hash: str | None = None,
) -> int:
    """Send a notification to all peer agents. Returns count notified."""
    agent_id = get_current_agent_id()
    auth_token = get_current_auth_token()
    if not agent_id or not auth_token:
        return 0

    peers = await get_peer_agents(same_project_only=True)
    notified = 0

    for peer in peers:
        port = peer.get("port", 0)
        if not port:
            continue
        try:
            url = f"http://127.0.0.1:{port}/agent/notify"
            await _http_post(url, {
                "source_agent_id": agent_id,
                "auth_token": auth_token,
                "from_product": get_current_product_type(),
                "notification_type": notification_type,
                "data": data,
            }, timeout=2.0)
            notified += 1
        except Exception:
            pass

    return notified
