"""Reactive agent base classes (minimal shim for routing).

This is a minimal actor-first adaptation to satisfy reactive routing imports.
Agents are expected to implement async `process_message`; this shim also offers
`process_message_sync` used by stream router mapping.
"""

from __future__ import annotations

import asyncio
from abc import ABC, abstractmethod
from typing import Any

from rx.core import Observable  # type: ignore[attr-defined]
from rx.subject import Subject  # type: ignore[attr-defined]

from cleveragents.core.exceptions import ExecutionError


class Agent(ABC):
    """Abstract base class for reactive agents."""

    def __init__(self, name: str, config: dict[str, Any] | None = None):
        self.name = name
        self.config = config or {}
        self.input_stream: Any = Subject()
        self.output_stream: Any = Subject()
        self._tasks: list[asyncio.Task[Any]] = []
        self._setup_processing_pipeline()

    def _setup_processing_pipeline(self) -> None:
        def _on_next(msg: Any) -> None:
            task = asyncio.create_task(self._process_wrapper(msg))
            self._tasks.append(task)

        self.input_stream.subscribe(
            on_next=_on_next,
            on_error=self.output_stream.on_error,
        )

    async def _process_wrapper(
        self, message_data: tuple[str, dict[str, Any]] | Any
    ) -> None:
        try:
            if isinstance(message_data, tuple):
                message, context = message_data
            else:
                message, context = message_data, {}
            result = await self.process_message(message, context)
            self.output_stream.on_next(result)
        except Exception as exc:  # pylint: disable=broad-except
            raise ExecutionError(f"Agent {self.name} processing failed: {exc}") from exc

    def process_message_sync(
        self, message: Any, context: dict[str, Any] | None = None
    ) -> Any:
        return asyncio.get_event_loop().run_until_complete(
            self.process_message(message, context or {})
        )

    @abstractmethod
    async def process_message(
        self, message: Any, context: dict[str, Any] | None = None
    ) -> Any: ...

    @abstractmethod
    def get_capabilities(self) -> list[str]: ...

    def get_metadata(self) -> dict[str, Any]:
        return {
            "name": self.name,
            "type": self.__class__.__name__,
            "capabilities": self.get_capabilities(),
            "reactive": True,
        }

    def send_message(self, message: Any, context: dict[str, Any] | None = None) -> None:
        self.input_stream.on_next((message, context or {}))

    def subscribe_to_output(self, observer: Any) -> None:
        self.output_stream.subscribe(observer)

    def create_observable(self) -> Observable:
        return self.output_stream.pipe()

    def dispose(self) -> None:
        if hasattr(self.input_stream, "dispose"):
            self.input_stream.dispose()
        if hasattr(self.output_stream, "dispose"):
            self.output_stream.dispose()


class AgentWithMemory(Agent):
    """Agent with simple in-memory state."""

    def __init__(self, name: str, config: dict[str, Any] | None = None):
        super().__init__(name, config)
        self.memory: dict[str, Any] = {}
        self._memory_lock = asyncio.Lock()

    async def remember(self, key: str, value: Any) -> None:
        async with self._memory_lock:
            self.memory[key] = value

    async def recall(self, key: str) -> Any:
        async with self._memory_lock:
            return self.memory.get(key)
