"""Mock EvalChannel implementation for unit tests.

Simulates send/poll_reply with configurable latency, failure modes, and
cancellation hooks. Does not touch the network.
"""

import asyncio
from typing import Any, Optional

from agenteval.channels.base import ChannelHealth, EvalChannel, Reply, SendResult


class MockChannel(EvalChannel):
    """Configurable in-memory channel for engine tests."""

    def __init__(
        self,
        reply_delay: float = 0.0,
        send_ok: bool = True,
        missing_reply: bool = False,
        reply_text: Optional[str] = None,
        raise_on_send: Optional[Exception] = None,
        raise_on_poll: Optional[Exception] = None,
    ):
        self.reply_delay = reply_delay
        self.send_ok = send_ok
        self.missing_reply = missing_reply
        self.reply_text = reply_text
        self.raise_on_send = raise_on_send
        self.raise_on_poll = raise_on_poll

        self.sent: list[str] = []
        self.send_calls = 0
        self.poll_calls = 0
        self._msg_counter = 0

    async def health_check(self) -> ChannelHealth:
        return ChannelHealth(ok=True, message="mock")

    async def _send(self, content: str, **kwargs: Any) -> SendResult:
        self.send_calls += 1
        self.sent.append(content)
        if self.raise_on_send:
            raise self.raise_on_send
        if not self.send_ok:
            return SendResult(ok=False, error="mock send failure")
        self._msg_counter += 1
        return SendResult(ok=True, question_msg_id=f"q-{self._msg_counter}")

    async def _poll_reply(
        self,
        question_msg_id: str,
        timeout: float = 30.0,
        poll_interval: float = 1.0,
    ) -> Optional[Reply]:
        self.poll_calls += 1
        if self.raise_on_poll:
            raise self.raise_on_poll
        if self.missing_reply:
            # Simulate a slow target: sleep past the timeout so the engine
            # observes a poll timeout.
            await asyncio.sleep(timeout + 0.05)
            return None
        if self.reply_delay:
            await asyncio.sleep(self.reply_delay)
        text = self.reply_text if self.reply_text is not None else f"echo: {question_msg_id}"
        return Reply(
            question_msg_id=question_msg_id,
            content=text,
            raw_message={"msgBody": {"content": text}},
        )
