"""
Mock factories for creating test doubles and mocks.
"""

from unittest.mock import Mock, AsyncMock, MagicMock
from typing import Dict, Any, List, Optional, Callable
import asyncio
from datetime import datetime, timezone


class MockRepositoryFactory:
    """Factory for creating mock repository objects."""

    @staticmethod
    def create_issue_repository() -> Mock:
        """Create a mock issue repository."""
        repo = AsyncMock()
        repo.get_issue = AsyncMock()
        repo.create_issue = AsyncMock()
        repo.update_issue = AsyncMock()
        repo.delete_issue = AsyncMock()
        repo.list_issues = AsyncMock()
        repo.search_issues = AsyncMock()
        return repo

    @staticmethod
    def create_project_repository() -> Mock:
        """Create a mock project repository."""
        repo = AsyncMock()
        repo.get_project = AsyncMock()
        repo.create_project = AsyncMock()
        repo.update_project = AsyncMock()
        repo.delete_project = AsyncMock()
        repo.list_projects = AsyncMock()
        repo.get_issue_project_info = AsyncMock()
        return repo

    @staticmethod
    def create_document_repository() -> Mock:
        """Create a mock document repository."""
        repo = AsyncMock()
        repo.store_document = AsyncMock()
        repo.get_document = AsyncMock()
        repo.update_document = AsyncMock()
        repo.delete_document = AsyncMock()
        repo.list_documents = AsyncMock()
        repo.search_content = AsyncMock()
        return repo


class MockServiceFactory:
    """Factory for creating mock service objects."""

    @staticmethod
    def create_http_client() -> Mock:
        """Create a mock HTTP client."""
        client = AsyncMock()

        # Default successful response
        mock_response = AsyncMock()
        mock_response.status = 200
        mock_response.json = AsyncMock(return_value={"status": "success"})
        mock_response.text = AsyncMock(return_value='{"status": "success"}')
        mock_response.headers = {"Content-Type": "application/json"}

        client.get.return_value = mock_response
        client.post.return_value = mock_response
        client.put.return_value = mock_response
        client.delete.return_value = mock_response
        client.close = AsyncMock()

        return client

    @staticmethod
    def create_database_connection() -> Mock:
        """Create a mock database connection."""
        conn = Mock()
        cursor = Mock()

        conn.cursor.return_value = cursor
        conn.execute.return_value = cursor
        conn.commit = Mock()
        conn.rollback = Mock()
        conn.close = Mock()

        # Default empty results
        cursor.fetchone.return_value = None
        cursor.fetchall.return_value = []
        cursor.fetchmany.return_value = []
        cursor.lastrowid = 1
        cursor.rowcount = 0

        return conn

    @staticmethod
    def create_cache_manager() -> Mock:
        """Create a mock cache manager."""
        cache = AsyncMock()
        cache.get = AsyncMock(return_value=None)
        cache.set = AsyncMock()
        cache.delete = AsyncMock()
        cache.clear = AsyncMock()
        cache.exists = AsyncMock(return_value=False)
        cache.expire = AsyncMock()
        return cache

    @staticmethod
    def create_file_system() -> Mock:
        """Create a mock file system."""
        fs = Mock()
        fs.read_file = Mock(return_value="mock file content")
        fs.write_file = Mock()
        fs.delete_file = Mock()
        fs.exists = Mock(return_value=True)
        fs.list_files = Mock(return_value=[])
        fs.create_directory = Mock()
        fs.delete_directory = Mock()
        return fs


class MockConfigFactory:
    """Factory for creating mock configuration objects."""

    @staticmethod
    def create_test_config(overrides: Optional[Dict[str, Any]] = None) -> Mock:
        """Create a mock configuration object."""
        config = Mock()

        # Default configuration values
        defaults = {
            "workspace_dir": "/tmp/test-workspace",
            "database_path": "/tmp/test.db",
            "cache_dir": "/tmp/test-cache",
            "gitea_url": "http://test-gitea.com",
            "gitea_token": "test-token",
            "repo_owner": "test",
            "repo_name": "repo",
            "log_level": "DEBUG",
            "max_retries": 3,
            "timeout": 30,
            "batch_size": 100
        }

        if overrides:
            defaults.update(overrides)

        for key, value in defaults.items():
            setattr(config, key, value)

        return config


class MockEventFactory:
    """Factory for creating mock event objects and event handlers."""

    @staticmethod
    def create_event_emitter() -> Mock:
        """Create a mock event emitter."""
        emitter = Mock()
        emitter.emit = Mock()
        emitter.on = Mock()
        emitter.off = Mock()
        emitter.once = Mock()
        emitter.listeners = Mock(return_value=[])
        return emitter

    @staticmethod
    def create_event_handler() -> Mock:
        """Create a mock event handler."""
        handler = Mock()
        handler.handle = AsyncMock()
        handler.can_handle = Mock(return_value=True)
        handler.priority = 1
        return handler


class MockNetworkFactory:
    """Factory for creating network-related mocks."""

    @staticmethod
    def create_rate_limiter() -> Mock:
        """Create a mock rate limiter."""
        limiter = AsyncMock()
        limiter.acquire = AsyncMock()
        limiter.release = AsyncMock()
        limiter.is_available = AsyncMock(return_value=True)
        limiter.reset = AsyncMock()
        return limiter

    @staticmethod
    def create_circuit_breaker() -> Mock:
        """Create a mock circuit breaker."""
        breaker = Mock()
        breaker.call = AsyncMock()
        breaker.is_open = Mock(return_value=False)
        breaker.is_closed = Mock(return_value=True)
        breaker.is_half_open = Mock(return_value=False)
        breaker.reset = Mock()
        return breaker


class MockTimeFactory:
    """Factory for creating time-related mocks."""

    @staticmethod
    def create_timer() -> Mock:
        """Create a mock timer."""
        timer = Mock()
        timer.start = Mock()
        timer.stop = Mock()
        timer.elapsed = 0.1
        timer.reset = Mock()
        return timer

    @staticmethod
    def create_scheduler() -> Mock:
        """Create a mock task scheduler."""
        scheduler = AsyncMock()
        scheduler.schedule = AsyncMock()
        scheduler.cancel = AsyncMock()
        scheduler.is_scheduled = Mock(return_value=False)
        scheduler.start = AsyncMock()
        scheduler.stop = AsyncMock()
        return scheduler


class MockResponseBuilder:
    """Builder for creating mock HTTP responses."""

    def __init__(self):
        self.status = 200
        self.headers = {"Content-Type": "application/json"}
        self.body = {"status": "success"}
        self.delay = 0.0
        self.exception = None

    def with_status(self, status: int) -> "MockResponseBuilder":
        """Set response status code."""
        self.status = status
        return self

    def with_headers(self, headers: Dict[str, str]) -> "MockResponseBuilder":
        """Set response headers."""
        self.headers.update(headers)
        return self

    def with_json_body(self, body: Dict[str, Any]) -> "MockResponseBuilder":
        """Set JSON response body."""
        self.body = body
        self.headers["Content-Type"] = "application/json"
        return self

    def with_text_body(self, body: str) -> "MockResponseBuilder":
        """Set text response body."""
        self.body = body
        self.headers["Content-Type"] = "text/plain"
        return self

    def with_delay(self, delay: float) -> "MockResponseBuilder":
        """Add delay to response."""
        self.delay = delay
        return self

    def with_exception(self, exception: Exception) -> "MockResponseBuilder":
        """Make response raise an exception."""
        self.exception = exception
        return self

    def build(self) -> Mock:
        """Build the mock response."""
        if self.exception:
            # Create a coroutine that raises the exception
            async def raise_exception():
                await asyncio.sleep(self.delay)
                raise self.exception
            return raise_exception()

        response = AsyncMock()
        response.status = self.status
        response.headers = self.headers

        if isinstance(self.body, dict):
            response.json = AsyncMock(return_value=self.body)
            response.text = AsyncMock(return_value=str(self.body))
        else:
            response.text = AsyncMock(return_value=self.body)
            response.json = AsyncMock(side_effect=ValueError("Not JSON"))

        # Add delay if specified
        if self.delay > 0:
            original_json = response.json
            original_text = response.text

            async def delayed_json():
                await asyncio.sleep(self.delay)
                return await original_json()

            async def delayed_text():
                await asyncio.sleep(self.delay)
                return await original_text()

            response.json = delayed_json
            response.text = delayed_text

        return response


# Convenience functions
def create_failing_mock(exception: Exception) -> Mock:
    """Create a mock that always raises the specified exception."""
    mock = Mock()
    mock.side_effect = exception
    return mock


def create_async_failing_mock(exception: Exception) -> AsyncMock:
    """Create an async mock that always raises the specified exception."""
    mock = AsyncMock()
    mock.side_effect = exception
    return mock


def create_sequence_mock(values: List[Any]) -> Mock:
    """Create a mock that returns values in sequence."""
    mock = Mock()
    mock.side_effect = values
    return mock


def create_async_sequence_mock(values: List[Any]) -> AsyncMock:
    """Create an async mock that returns values in sequence."""
    mock = AsyncMock()
    mock.side_effect = values
    return mock


def create_conditional_mock(condition: Callable[..., bool], true_value: Any, false_value: Any) -> Mock:
    """Create a mock that returns different values based on a condition."""
    def side_effect(*args, **kwargs):
        if condition(*args, **kwargs):
            return true_value
        return false_value

    mock = Mock()
    mock.side_effect = side_effect
    return mock