# File: tests/unit/infrastructure/json/mcp/test_integration_phase5.py
# Purpose: Integration tests for Phase 5 (JSONConversionServer Facade redesign)
# Context: Validates integration of ToolRegistry, ToolExecutor, CacheManager, and PerformanceMonitor

"""Integration tests for Phase 5 JSONConversionServer facade redesign.

Tests cover:
- ToolRegistry and JSONConversionServer integration
- ToolExecutor and component orchestration
- PerformanceMonitor and execution metrics
- CacheManager lifecycle management
- Unified component initialization
"""

from pathlib import Path
from unittest.mock import Mock

import pytest

from noveler.infrastructure.json.mcp.servers.cache_manager import CacheManager
from noveler.infrastructure.json.mcp.servers.performance_monitor import PerformanceMonitor
from noveler.infrastructure.json.mcp.servers.tool_executor import ExecutionContext, ToolExecutor
from noveler.infrastructure.json.mcp.servers.tool_registry import ToolDefinition, ToolRegistry


@pytest.fixture
def mock_logger():
    """Create a mock logger for testing."""
    return Mock()


@pytest.fixture
def tool_registry(mock_logger):
    """Create a ToolRegistry instance for testing."""
    return ToolRegistry(logger=mock_logger)


@pytest.fixture
def cache_manager(mock_logger):
    """Create a CacheManager instance for testing."""
    return CacheManager(logger=mock_logger)


@pytest.fixture
def tool_executor(mock_logger):
    """Create a ToolExecutor instance for testing."""
    return ToolExecutor(logger=mock_logger)


@pytest.fixture
def performance_monitor(mock_logger):
    """Create a PerformanceMonitor instance for testing."""
    return PerformanceMonitor(logger=mock_logger)


class TestToolRegistryIntegration:
    """Test ToolRegistry integration with other components."""

    def test_tool_registry_initialization_with_other_components(
        self, tool_registry, cache_manager, tool_executor, performance_monitor
    ):
        """Test that ToolRegistry initializes alongside other components."""
        assert tool_registry is not None
        assert cache_manager is not None
        assert tool_executor is not None
        assert performance_monitor is not None

    def test_tool_registry_can_store_definitions(self, tool_registry):
        """Test that ToolRegistry can store and retrieve tool definitions."""
        def dummy_handler():
            return {"status": "ok"}

        tool_def = ToolDefinition(
            name="test_tool",
            handler=dummy_handler,
            input_schema={"type": "object"},
            description="Test tool"
        )

        tool_registry.register_tool(tool_def)
        retrieved = tool_registry.get_tool_definition("test_tool")

        assert retrieved is not None
        assert retrieved.name == "test_tool"
        assert retrieved.handler == dummy_handler

    def test_get_all_tool_definitions(self, tool_registry):
        """Test retrieval of all tool definitions."""
        def handler1():
            return {"id": 1}

        def handler2():
            return {"id": 2}

        tool_registry.register_tool(ToolDefinition(
            name="tool1",
            handler=handler1,
            input_schema={"type": "object"},
            description="Tool 1"
        ))

        tool_registry.register_tool(ToolDefinition(
            name="tool2",
            handler=handler2,
            input_schema={"type": "object"},
            description="Tool 2"
        ))

        all_definitions = tool_registry.get_all_tool_definitions()

        assert len(all_definitions) == 2
        assert "tool1" in all_definitions
        assert "tool2" in all_definitions


class TestComponentOrchestration:
    """Test orchestration of multiple components together."""

    def test_tool_executor_with_tool_registry(self, tool_executor, tool_registry, mock_logger):
        """Test ToolExecutor with tools from ToolRegistry."""
        def test_handler(x: int) -> dict:
            return {"result": x * 2}

        tool_def = ToolDefinition(
            name="multiply",
            handler=test_handler,
            input_schema={"type": "object"},
            description="Multiply input by 2"
        )

        tool_registry.register_tool(tool_def)
        handler = tool_registry.get_tool("multiply")

        context = ExecutionContext(tool_name="multiply")
        result = tool_executor.execute(handler, {"x": 5}, context)

        assert result.output == {"result": 10}

    def test_performance_monitor_with_tool_executor(
        self, tool_executor, performance_monitor
    ):
        """Test PerformanceMonitor tracking tool execution."""
        def sample_handler():
            return {"status": "success"}

        context = ExecutionContext(tool_name="sample_tool")
        result = tool_executor.execute(sample_handler, {}, context)

        # Convert ExecutionStatus enum to string for PerformanceMonitor
        status_str = result.status.value if hasattr(result.status, "value") else str(result.status)
        performance_monitor.record_execution(
            tool_name="sample_tool",
            duration_ms=result.duration_ms,
            status=status_str,
            error=result.error
        )

        metrics = performance_monitor.get_metrics()

        assert metrics.total_executions == 1
        assert metrics.successful_executions == 1

    def test_cache_manager_with_file_operations(self, cache_manager):
        """Test CacheManager with typical file caching scenarios."""
        def file_loader(path: Path):
            return {"path": str(path), "content": "cached"}

        path1 = Path("/tmp/file1.txt")
        path2 = Path("/tmp/file2.txt")

        result1 = cache_manager.file_cache_get(path1, file_loader)
        result2 = cache_manager.file_cache_get(path1, file_loader)  # Cache hit

        assert result1 == result2
        assert result1["path"] == str(path1)

        stats = cache_manager.get_statistics()
        assert stats.file_cache_size == 1


class TestComponentInteroperability:
    """Test interoperability between components."""

    def test_all_components_use_same_logger(self, mock_logger):
        """Test that all components can share the same logger."""
        registry = ToolRegistry(logger=mock_logger)
        cache = CacheManager(logger=mock_logger)
        executor = ToolExecutor(logger=mock_logger)
        monitor = PerformanceMonitor(logger=mock_logger)

        assert registry._logger is mock_logger
        assert cache._logger is mock_logger
        assert executor._logger is mock_logger
        assert monitor._logger is mock_logger

    def test_tool_definition_compatibility_with_executor(self, tool_registry, tool_executor):
        """Test that tool definitions from registry work with executor."""
        def process_data(data: str) -> str:
            return data.upper()

        tool_def = ToolDefinition(
            name="uppercase",
            handler=process_data,
            input_schema={
                "type": "object",
                "properties": {"data": {"type": "string"}}
            },
            description="Convert to uppercase"
        )

        tool_registry.register_tool(tool_def)
        handler = tool_registry.get_tool("uppercase")

        context = ExecutionContext(tool_name="uppercase")
        result = tool_executor.execute(handler, {"data": "hello"}, context)

        assert result.output == "HELLO"

    def test_cache_hit_with_performance_tracking(self, cache_manager, performance_monitor):
        """Test cache operations with performance monitoring."""
        call_count = 0

        def expensive_loader(path: Path):
            nonlocal call_count
            call_count += 1
            return {"expensive": "data", "call": call_count}

        path = Path("/data/file.txt")

        # First call: cache miss
        result1 = cache_manager.file_cache_get(path, expensive_loader)
        performance_monitor.record_cache_miss("FileIOCache")  # miss

        # Second call: cache hit
        result2 = cache_manager.file_cache_get(path, expensive_loader)
        performance_monitor.record_cache_hit("FileIOCache")  # hit

        assert result1 == result2
        assert call_count == 1  # Only called once

        metrics = performance_monitor.get_metrics()
        assert metrics.total_executions >= 0


class TestPhase5Integration:
    """Test Phase 5 specific integration patterns."""

    def test_component_initialization_sequence(self, mock_logger):
        """Test proper initialization sequence of all components."""
        registry = ToolRegistry(logger=mock_logger)
        cache = CacheManager(logger=mock_logger)
        executor = ToolExecutor(logger=mock_logger)
        monitor = PerformanceMonitor(logger=mock_logger)

        # Verify all components initialized
        assert registry._tools == {}
        assert len(cache._file_cache) == 0
        assert executor._execution_count == 0
        assert monitor._execution_history == []

    def test_unified_error_handling_across_components(
        self, tool_executor, cache_manager, performance_monitor
    ):
        """Test unified error handling patterns across components."""
        def failing_handler():
            raise ValueError("Handler failure")

        context = ExecutionContext(tool_name="failing_tool", retry_count=0)
        result = tool_executor.execute(failing_handler, {}, context)

        # Convert ExecutionStatus enum to string for PerformanceMonitor
        status_str = result.status.value if hasattr(result.status, "value") else str(result.status)
        # Record the failure in performance monitor
        performance_monitor.record_execution(
            tool_name="failing_tool",
            duration_ms=result.duration_ms,
            status=status_str,
            error=result.error
        )

        metrics = performance_monitor.get_metrics()
        assert metrics.total_executions == 1
        assert metrics.failed_executions == 1

    def test_facade_pattern_compatibility(self, tool_registry, tool_executor, mock_logger):
        """Test that components support facade pattern for JSONConversionServer."""
        # Register a tool via registry
        def facade_tool(input_data: str) -> str:
            return f"Processed: {input_data}"

        tool_registry.register_tool(ToolDefinition(
            name="facade_test",
            handler=facade_tool,
            input_schema={"type": "object"},
            description="Facade test tool"
        ))

        # Execute via executor
        handler = tool_registry.get_tool("facade_test")
        context = ExecutionContext(tool_name="facade_test")
        result = tool_executor.execute(handler, {"input_data": "test"}, context)

        assert result.output == "Processed: test"

    @pytest.mark.asyncio
    async def test_async_component_coordination(self, performance_monitor):
        """Test async coordination of performance monitoring."""
        # Simulate monitoring startup
        import asyncio

        monitor_task = asyncio.create_task(
            performance_monitor.start_monitoring()
        )

        # Simulate some executions
        await asyncio.sleep(0.1)
        performance_monitor.record_execution(
            tool_name="async_tool",
            duration_ms=50.0,
            status="success"  # Use string instead of enum
        )

        # Cancel monitoring
        monitor_task.cancel()
        try:
            await monitor_task
        except asyncio.CancelledError:
            pass

        metrics = performance_monitor.get_metrics()
        assert metrics.total_executions >= 1


class TestPhase5Completeness:
    """Test Phase 5 completeness criteria."""

    def test_all_four_components_functional(
        self, tool_registry, cache_manager, tool_executor, performance_monitor
    ):
        """Test that all Phase 1-4 components are functional and integrated."""
        # Phase 1: ToolRegistry
        assert hasattr(tool_registry, "_tools")
        assert hasattr(tool_registry, "register_tool")
        assert hasattr(tool_registry, "get_tool")
        assert hasattr(tool_registry, "list_tools")
        assert hasattr(tool_registry, "get_all_tool_definitions")

        # Phase 2: CacheManager
        assert hasattr(cache_manager, "_file_cache")
        assert hasattr(cache_manager, "_json_conversion_cache")
        assert hasattr(cache_manager, "_path_resolution_cache")
        assert hasattr(cache_manager, "file_cache_get")
        assert hasattr(cache_manager, "json_cache_set")
        assert hasattr(cache_manager, "cleanup_expired")

        # Phase 3: ToolExecutor
        assert hasattr(tool_executor, "_execution_count")
        assert hasattr(tool_executor, "execute")
        assert hasattr(tool_executor, "execute_async")
        assert hasattr(tool_executor, "get_statistics")

        # Phase 4: PerformanceMonitor
        assert hasattr(performance_monitor, "_execution_history")
        assert hasattr(performance_monitor, "record_execution")
        assert hasattr(performance_monitor, "get_metrics")
        assert hasattr(performance_monitor, "start_monitoring")

    def test_phase5_integration_points(self, tool_registry, tool_executor, performance_monitor):
        """Test key Phase 5 integration points."""
        # Tool definition + Executor integration
        def integrated_handler(x: int) -> int:
            return x + 1

        tool_def = ToolDefinition(
            name="integrated",
            handler=integrated_handler,
            input_schema={"type": "object"},
            description="Integrated tool"
        )

        tool_registry.register_tool(tool_def)
        handler = tool_registry.get_tool("integrated")

        context = ExecutionContext(tool_name="integrated")
        result = tool_executor.execute(handler, {"x": 5}, context)

        # Convert ExecutionStatus enum to string for PerformanceMonitor
        status_str = result.status.value if hasattr(result.status, "value") else str(result.status)
        # Track in performance monitor
        performance_monitor.record_execution(
            tool_name="integrated",
            duration_ms=result.duration_ms,
            status=status_str
        )

        # Verify integration
        assert result.output == 6
        metrics = performance_monitor.get_metrics()
        assert metrics.total_executions == 1
        assert metrics.success_rate == 100.0  # Percentage, not fraction
