"""Testing utilities and helpers."""

import unittest
from typing import Any, Dict, List, Optional
from unittest.mock import AsyncMock, MagicMock

from pygeai_orchestration.core.base import BasePattern, PatternConfig, PatternResult, PatternType


class MockAgent:
    """Mock agent for testing."""

    def __init__(self, responses: Optional[List[str]] = None):
        """Initialize mock agent.

        Args:
            responses: Pre-defined responses
        """
        self.responses = responses or ["Mock response"]
        self.call_count = 0
        self.calls: List[Dict[str, Any]] = []

    async def generate(self, prompt: str, **kwargs) -> str:
        """Generate mock response.

        Args:
            prompt: Input prompt
            **kwargs: Additional arguments

        Returns:
            Mock response
        """
        self.calls.append({
            "prompt": prompt,
            "kwargs": kwargs,
            "call_number": self.call_count
        })

        response_index = min(self.call_count, len(self.responses) - 1)
        response = self.responses[response_index]
        self.call_count += 1

        return response

    async def chat(self, messages: List[Dict[str, str]], **kwargs) -> str:
        """Mock chat method.

        Args:
            messages: Chat messages
            **kwargs: Additional arguments

        Returns:
            Mock response
        """
        last_message = messages[-1]["content"] if messages else ""
        return await self.generate(last_message, **kwargs)

    def reset(self) -> None:
        """Reset mock agent state."""
        self.call_count = 0
        self.calls.clear()


class MockPattern(BasePattern):
    """Mock pattern for testing."""

    def __init__(
        self,
        name: str = "MockPattern",
        result: str = "Mock result",
        success: bool = True,
        iterations: int = 1
    ):
        """Initialize mock pattern.

        Args:
            name: Pattern name
            result: Result to return
            success: Success status
            iterations: Number of iterations
        """
        config = PatternConfig(
            name=name,
            pattern_type=PatternType.REFLECTION
        )
        super().__init__(config)

        self.agent = MockAgent([result])
        self.mock_result = result
        self.mock_success = success
        self.mock_iterations = iterations
        self.execution_count = 0

    async def execute(self, task: str, **kwargs) -> PatternResult:
        """Execute mock pattern.

        Args:
            task: Task to execute
            **kwargs: Additional arguments

        Returns:
            Mock result
        """
        self.execution_count += 1

        return PatternResult(
            success=self.mock_success,
            result=self.mock_result,
            iterations=self.mock_iterations,
            metadata={"execution_count": self.execution_count}
        )

    async def step(self, task: str, context=None) -> PatternResult:
        """Execute one step.

        Args:
            task: Task to execute
            context: Optional context

        Returns:
            Mock result
        """
        return await self.execute(task)


class PatternTestCase(unittest.IsolatedAsyncioTestCase):
    """Base test case for pattern testing."""

    def create_mock_agent(self, responses: Optional[List[str]] = None) -> MockAgent:
        """Create mock agent for testing.

        Args:
            responses: Pre-defined responses

        Returns:
            Mock agent instance
        """
        return MockAgent(responses)

    def assert_pattern_result(
        self,
        result: PatternResult,
        success: Optional[bool] = None,
        has_result: bool = True,
        min_iterations: int = 1
    ) -> None:
        """Assert pattern result properties.

        Args:
            result: Pattern result to check
            success: Expected success status
            has_result: Whether result should have output
            min_iterations: Minimum iterations
        """
        self.assertIsInstance(result, PatternResult)

        if success is not None:
            self.assertEqual(result.success, success)

        if has_result:
            self.assertIsNotNone(result.result)
            self.assertNotEqual(result.result, "")

        self.assertGreaterEqual(result.iterations, min_iterations)

    def assert_agent_called(
        self,
        agent: MockAgent,
        min_calls: int = 1,
        contains: Optional[str] = None
    ) -> None:
        """Assert agent was called correctly.

        Args:
            agent: Mock agent
            min_calls: Minimum number of calls
            contains: Text that should be in prompts
        """
        self.assertGreaterEqual(agent.call_count, min_calls)

        if contains:
            prompts = [call["prompt"] for call in agent.calls]
            self.assertTrue(
                any(contains in prompt for prompt in prompts),
                f"No prompt contains '{contains}'"
            )


def create_test_pattern(
    pattern_class: type,
    agent_responses: Optional[List[str]] = None,
    config_overrides: Optional[Dict[str, Any]] = None
) -> BasePattern:
    """Create pattern instance for testing.

    Args:
        pattern_class: Pattern class to instantiate
        agent_responses: Mock agent responses
        config_overrides: Configuration overrides

    Returns:
        Pattern instance
    """
    config_dict = {
        "name": pattern_class.__name__,
        "pattern_type": PatternType.REFLECTION
    }

    if config_overrides:
        config_dict.update(config_overrides)

    config = PatternConfig(**config_dict)
    pattern = pattern_class(config)

    if agent_responses:
        pattern.agent = MockAgent(agent_responses)

    return pattern


class AsyncMockHelper:
    """Helper for creating async mocks."""

    @staticmethod
    def create_async_mock(return_value: Any = None) -> AsyncMock:
        """Create async mock with return value.

        Args:
            return_value: Value to return

        Returns:
            Async mock
        """
        mock = AsyncMock()
        mock.return_value = return_value
        return mock

    @staticmethod
    def create_agent_mock(responses: List[str]) -> MagicMock:
        """Create mock agent with multiple responses.

        Args:
            responses: List of responses

        Returns:
            Mock agent
        """
        agent = MagicMock()
        agent.generate = AsyncMock(side_effect=responses)
        return agent


class TestDataBuilder:
    """Builder for test data."""

    @staticmethod
    def build_pattern_result(
        success: bool = True,
        result: str = "Test result",
        iterations: int = 1,
        **metadata
    ) -> PatternResult:
        """Build pattern result for testing.

        Args:
            success: Success status
            result: Result string
            iterations: Number of iterations
            **metadata: Additional metadata

        Returns:
            Pattern result
        """
        return PatternResult(
            success=success,
            result=result,
            iterations=iterations,
            metadata=metadata
        )

    @staticmethod
    def build_pattern_config(
        name: str = "TestPattern",
        pattern_type: PatternType = PatternType.REFLECTION,
        **kwargs
    ) -> PatternConfig:
        """Build pattern config for testing.

        Args:
            name: Pattern name
            pattern_type: Pattern type
            **kwargs: Additional config

        Returns:
            Pattern config
        """
        return PatternConfig(
            name=name,
            pattern_type=pattern_type,
            **kwargs
        )
