"""
Tests for engine/pipeline/adapters.py - Stage adapters for the pipeline.

Tests Stage adapters that bridge existing components to the Stage interface.
Focuses on behavior testing rather than mock interactions.
"""

from unittest.mock import MagicMock

from engine.data_sources.sources import SourceItem
from engine.display.backends.null import NullDisplay
from engine.effects.plugins import discover_plugins
from engine.effects.registry import get_registry
from engine.pipeline.adapters import (
    DataSourceStage,
    DisplayStage,
    EffectPluginStage,
    PassthroughStage,
    SourceItemsToBufferStage,
)
from engine.pipeline.core import PipelineContext


class TestDataSourceStage:
    """Test DataSourceStage adapter."""

    def test_datasource_stage_properties(self):
        """DataSourceStage has correct name, category, and capabilities."""
        mock_source = MagicMock()
        stage = DataSourceStage(mock_source, name="headlines")

        assert stage.name == "headlines"
        assert stage.category == "source"
        assert "source.headlines" in stage.capabilities
        assert stage.dependencies == set()

    def test_datasource_stage_process_calls_get_items(self):
        """DataSourceStage.process() calls source.get_items()."""
        mock_items = [
            SourceItem(content="Item 1", source="headlines", timestamp="12:00"),
        ]
        mock_source = MagicMock()
        mock_source.get_items.return_value = mock_items

        stage = DataSourceStage(mock_source, name="headlines")
        ctx = PipelineContext()
        result = stage.process(None, ctx)

        assert result == mock_items
        mock_source.get_items.assert_called_once()

    def test_datasource_stage_process_fallback(self):
        """DataSourceStage.process() returns data if no get_items method."""
        mock_source = MagicMock(spec=[])  # No get_items method
        stage = DataSourceStage(mock_source, name="headlines")
        ctx = PipelineContext()
        test_data = [{"content": "test"}]

        result = stage.process(test_data, ctx)
        assert result == test_data


class TestDisplayStage:
    """Test DisplayStage adapter using NullDisplay for real behavior."""

    def test_display_stage_properties(self):
        """DisplayStage has correct name, category, and capabilities."""
        display = NullDisplay()
        stage = DisplayStage(display, name="terminal")

        assert stage.name == "terminal"
        assert stage.category == "display"
        assert "display.output" in stage.capabilities
        assert "render.output" in stage.dependencies

    def test_display_stage_init_and_process(self):
        """DisplayStage initializes display and processes buffer."""
        from engine.pipeline.params import PipelineParams

        display = NullDisplay()
        stage = DisplayStage(display, name="terminal")

        ctx = PipelineContext()
        ctx.params = PipelineParams()
        ctx.params.viewport_width = 80
        ctx.params.viewport_height = 24

        # Initialize
        result = stage.init(ctx)
        assert result is True

        # Process buffer
        buffer = ["Line 1", "Line 2", "Line 3"]
        output = stage.process(buffer, ctx)
        assert output == buffer

        # Verify display captured the buffer
        assert display._last_buffer == buffer

    def test_display_stage_skips_none_data(self):
        """DisplayStage.process() skips show() if data is None."""
        display = NullDisplay()
        stage = DisplayStage(display, name="terminal")

        ctx = PipelineContext()
        result = stage.process(None, ctx)

        assert result is None
        assert display._last_buffer is None


class TestPassthroughStage:
    """Test PassthroughStage adapter."""

    def test_passthrough_stage_properties(self):
        """PassthroughStage has correct properties."""
        stage = PassthroughStage(name="test")

        assert stage.name == "test"
        assert stage.category == "render"
        assert stage.optional is True
        assert "render.output" in stage.capabilities
        assert "source" in stage.dependencies

    def test_passthrough_stage_process_unchanged(self):
        """PassthroughStage.process() returns data unchanged."""
        stage = PassthroughStage()
        ctx = PipelineContext()

        test_data = [
            SourceItem(content="Line 1", source="test", timestamp="12:00"),
        ]
        result = stage.process(test_data, ctx)

        assert result == test_data
        assert result is test_data


class TestSourceItemsToBufferStage:
    """Test SourceItemsToBufferStage adapter."""

    def test_source_items_to_buffer_stage_properties(self):
        """SourceItemsToBufferStage has correct properties."""
        stage = SourceItemsToBufferStage(name="custom-name")

        assert stage.name == "custom-name"
        assert stage.category == "render"
        assert stage.optional is True
        assert "render.output" in stage.capabilities
        assert "source" in stage.dependencies

    def test_source_items_to_buffer_stage_process_single_line(self):
        """SourceItemsToBufferStage converts single-line SourceItem."""
        stage = SourceItemsToBufferStage()
        ctx = PipelineContext()

        items = [
            SourceItem(content="Single line content", source="test", timestamp="12:00"),
        ]
        result = stage.process(items, ctx)

        assert isinstance(result, list)
        assert len(result) >= 1
        assert all(isinstance(line, str) for line in result)
        assert "Single line content" in result[0]

    def test_source_items_to_buffer_stage_process_multiline(self):
        """SourceItemsToBufferStage splits multiline SourceItem content."""
        stage = SourceItemsToBufferStage()
        ctx = PipelineContext()

        content = "Line 1\nLine 2\nLine 3"
        items = [
            SourceItem(content=content, source="test", timestamp="12:00"),
        ]
        result = stage.process(items, ctx)

        # Should have at least 3 lines
        assert len(result) >= 3
        assert all(isinstance(line, str) for line in result)

    def test_source_items_to_buffer_stage_process_multiple_items(self):
        """SourceItemsToBufferStage handles multiple SourceItems."""
        stage = SourceItemsToBufferStage()
        ctx = PipelineContext()

        items = [
            SourceItem(content="Item 1", source="test", timestamp="12:00"),
            SourceItem(content="Item 2", source="test", timestamp="12:01"),
            SourceItem(content="Item 3", source="test", timestamp="12:02"),
        ]
        result = stage.process(items, ctx)

        # Should have at least 3 lines (one per item, possibly more)
        assert len(result) >= 3
        assert all(isinstance(line, str) for line in result)


class TestEffectPluginStage:
    """Test EffectPluginStage adapter with real effect plugins."""

    def test_effect_plugin_stage_properties(self):
        """EffectPluginStage has correct properties for real effects."""
        discover_plugins()
        registry = get_registry()
        effect = registry.get("noise")

        stage = EffectPluginStage(effect, name="noise")

        assert stage.name == "noise"
        assert stage.category == "effect"
        assert stage.optional is False
        assert "effect.noise" in stage.capabilities

    def test_effect_plugin_stage_hud_special_handling(self):
        """EffectPluginStage has special handling for HUD effect."""
        discover_plugins()
        registry = get_registry()
        hud_effect = registry.get("hud")

        stage = EffectPluginStage(hud_effect, name="hud")

        assert stage.stage_type == "overlay"
        assert stage.is_overlay is True
        assert stage.render_order == 100

    def test_effect_plugin_stage_process_real_effect(self):
        """EffectPluginStage.process() calls real effect.process()."""
        from engine.pipeline.params import PipelineParams

        discover_plugins()
        registry = get_registry()
        effect = registry.get("noise")

        stage = EffectPluginStage(effect, name="noise")
        ctx = PipelineContext()
        ctx.params = PipelineParams()
        ctx.params.viewport_width = 80
        ctx.params.viewport_height = 24
        ctx.params.frame_number = 0

        test_buffer = ["Line 1", "Line 2", "Line 3"]
        result = stage.process(test_buffer, ctx)

        # Should return a list (possibly modified buffer)
        assert isinstance(result, list)
        # Noise effect should preserve line count
        assert len(result) == len(test_buffer)

    def test_effect_plugin_stage_process_with_real_figment(self):
        """EffectPluginStage processes figment effect correctly."""
        from engine.pipeline.params import PipelineParams

        discover_plugins()
        registry = get_registry()
        figment = registry.get("figment")

        stage = EffectPluginStage(figment, name="figment")
        ctx = PipelineContext()
        ctx.params = PipelineParams()
        ctx.params.viewport_width = 80
        ctx.params.viewport_height = 24
        ctx.params.frame_number = 0

        test_buffer = ["Line 1", "Line 2", "Line 3"]
        result = stage.process(test_buffer, ctx)

        # Figment is an overlay effect
        assert stage.is_overlay is True
        assert stage.stage_type == "overlay"
        # Result should be a list
        assert isinstance(result, list)
