# Copyright (c) Microsoft. All rights reserved.

import asyncio
import sys
from collections.abc import AsyncIterable, Awaitable, Callable

from semantic_kernel.agents.agent import Agent, AgentResponseItem, AgentThread
from semantic_kernel.agents.runtime.core.core_runtime import CoreRuntime
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.contents.streaming_chat_message_content import StreamingChatMessageContent
from semantic_kernel.contents.utils.author_role import AuthorRole

if sys.version_info >= (3, 12):
    from typing import override  # pragma: no cover
else:
    from typing_extensions import override  # pragma: no cover


class MockAgentThread(AgentThread):
    """A mock agent thread for testing purposes."""

    @override
    async def _create(self) -> str:
        return "mock_thread_id"

    @override
    async def _delete(self) -> None:
        pass

    @override
    async def _on_new_message(self, new_message: ChatMessageContent) -> None:
        pass


class MockAgent(Agent):
    """A mock agent for testing purposes."""

    @override
    async def get_response(
        self,
        messages: str | ChatMessageContent | list[str | ChatMessageContent] | None = None,
        *,
        thread: AgentThread | None = None,
        **kwargs,
    ) -> AgentResponseItem[ChatMessageContent]:
        pass

    @override
    async def invoke(
        self,
        messages: str | ChatMessageContent | list[str | ChatMessageContent] | None = None,
        *,
        thread: AgentThread | None = None,
        on_intermediate_message: Callable[[ChatMessageContent], Awaitable[None]] | None = None,
        **kwargs,
    ) -> AgentResponseItem[ChatMessageContent]:
        pass

    @override
    async def invoke_stream(
        self,
        messages: str | ChatMessageContent | list[str | ChatMessageContent] | None = None,
        *,
        thread: AgentThread | None = None,
        on_intermediate_message: Callable[[ChatMessageContent], Awaitable[None]] | None = None,
        **kwargs,
    ) -> AsyncIterable[AgentResponseItem[StreamingChatMessageContent]]:
        """Simulate streaming response from the agent."""
        # Simulate some processing time
        await asyncio.sleep(0.05)
        yield AgentResponseItem[StreamingChatMessageContent](
            message=StreamingChatMessageContent(
                role=AuthorRole.ASSISTANT,
                name=self.name,
                content="mock",
                choice_index=0,
            ),
            thread=thread or MockAgentThread(),
        )
        # Simulate some processing time before sending the next part of the response
        await asyncio.sleep(0.05)
        yield AgentResponseItem[StreamingChatMessageContent](
            message=StreamingChatMessageContent(
                role=AuthorRole.ASSISTANT,
                name=self.name,
                content="_response",
                choice_index=0,
            ),
            thread=thread or MockAgentThread(),
        )


class MockAgentWithException(MockAgent):
    """A mock agent that raises an exception for testing purposes."""

    @override
    async def invoke_stream(
        self,
        messages: str | ChatMessageContent | list[str | ChatMessageContent] | None = None,
        *,
        thread: AgentThread | None = None,
        on_intermediate_message: Callable[[ChatMessageContent], Awaitable[None]] | None = None,
        **kwargs,
    ) -> AsyncIterable[AgentResponseItem[StreamingChatMessageContent]]:
        """Simulate streaming response from the agent that raises an exception."""
        # Simulate some processing time
        await asyncio.sleep(0.05)

        yield AgentResponseItem[StreamingChatMessageContent](
            message=StreamingChatMessageContent(
                role=AuthorRole.ASSISTANT,
                name=self.name,
                content="mock",
                choice_index=0,
            ),
            thread=thread or MockAgentThread(),
        )

        raise RuntimeError("Mock agent exception")


class MockRuntime(CoreRuntime):
    """A mock agent runtime for testing purposes."""

    pass
