"""
Auto-capture of Claude Agent SDK message stream.

When running inside Outpost9, this module patches ClaudeSDKClient.receive_response
to forward every message (text, tool calls, results, subagent activity) directly
to the Outpost9 log collector.

No user code changes required. Just streams the raw SDK output as-is.
"""

import dataclasses
import json
import threading
import time
import urllib.request
import urllib.error
from typing import Any, Dict, List, Optional


class MessageForwarder:
    """
    Batches and forwards SDK messages to the log collector.
    Uses a background thread to avoid blocking the message stream.
    """

    def __init__(self, agent_id: str, log_url: str):
        self.agent_id = agent_id
        self.log_url = log_url
        self._buffer: List[Dict[str, Any]] = []
        self._lock = threading.Lock()
        self._running = True
        self._run_id: Optional[str] = None

        # Start background sender
        self._thread = threading.Thread(target=self._sender_loop, daemon=True)
        self._thread.start()

    def set_run_id(self, run_id: Optional[str]) -> None:
        self._run_id = run_id

    def forward(self, msg: Any) -> None:
        """Serialize and buffer a message for sending."""
        try:
            msg_type = type(msg).__name__
            # Use dataclasses.asdict for clean serialization
            if dataclasses.is_dataclass(msg):
                data = self._serialize(dataclasses.asdict(msg))
            else:
                data = self._serialize(vars(msg) if hasattr(msg, '__dict__') else {"raw": str(msg)})

            # Send as SDK type with full structured data in metadata
            entry = {
                "type": "sdk",
                "event": msg_type,
                "metadata": data,
            }

            with self._lock:
                self._buffer.append(entry)
        except Exception:
            pass  # Never break the user's message stream

    def _serialize(self, obj: Any) -> Any:
        """Recursively serialize for JSON compatibility."""
        if obj is None or isinstance(obj, (str, int, float, bool)):
            return obj
        if isinstance(obj, (list, tuple)):
            return [self._serialize(x) for x in obj]
        if isinstance(obj, dict):
            return {k: self._serialize(v) for k, v in obj.items()}
        return str(obj)

    def _sender_loop(self) -> None:
        """Background loop that flushes buffered messages."""
        while self._running:
            time.sleep(0.1)
            self._flush()

    def _flush(self) -> None:
        """Send buffered messages to the collector."""
        with self._lock:
            if not self._buffer:
                return
            batch = self._buffer[:]
            self._buffer.clear()

        # Get run ID (may be set by auto_logging)
        run_id = self._run_id
        if not run_id:
            try:
                from .auto_logging import get_run_id
                run_id = get_run_id()
            except Exception:
                pass

        payload: Dict[str, Any] = {
            "agentId": self.agent_id,
            "logs": batch,
        }
        if run_id:
            payload["runId"] = run_id

        try:
            data = json.dumps(payload, default=str).encode("utf-8")
            req = urllib.request.Request(
                self.log_url,
                data=data,
                headers={
                    "Content-Type": "application/json",
                    "User-Agent": "Outpost9-SDK/0.0.1",
                },
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=5) as resp:
                resp.read()
        except Exception:
            pass  # Silently ignore

    def stop(self) -> None:
        self._running = False
        self._thread.join(timeout=1)
        self._flush()  # Final flush


# Module-level forwarder
_forwarder: Optional[MessageForwarder] = None


def setup_message_capture(agent_id: str, log_url: str) -> None:
    """
    Patch ClaudeSDKClient.receive_response to forward all messages.
    Called automatically when the SDK detects Outpost9 environment.
    """
    global _forwarder

    try:
        from claude_agent_sdk import ClaudeSDKClient
    except ImportError:
        return  # Claude Agent SDK not installed, skip

    _forwarder = MessageForwarder(agent_id, log_url)

    # Monkey-patch receive_response
    original_receive = ClaudeSDKClient.receive_response

    async def patched_receive(self):
        async for msg in original_receive(self):
            _forwarder.forward(msg)
            yield msg

    ClaudeSDKClient.receive_response = patched_receive

    print("[outpost9] Message stream capture enabled")
