"""Integration test: FrameBufferStage in the pipeline."""

import queue

from engine.data_sources.sources import ListDataSource, SourceItem
from engine.effects.types import EffectConfig
from engine.pipeline import Pipeline, PipelineConfig
from engine.pipeline.adapters import (
    DataSourceStage,
    DisplayStage,
    SourceItemsToBufferStage,
)
from engine.pipeline.core import PipelineContext
from engine.pipeline.stages.framebuffer import FrameBufferStage


class QueueDisplay:
    """Stub display that captures every frame into a queue."""

    def __init__(self):
        self.frames: queue.Queue[list[str]] = queue.Queue()
        self.width = 80
        self.height = 24
        self._init_called = False

    def init(self, width: int, height: int, reuse: bool = False) -> None:
        self.width = width
        self.height = height
        self._init_called = True

    def show(self, buffer: list[str], border: bool = False) -> None:
        self.frames.put(list(buffer))

    def clear(self) -> None:
        pass

    def cleanup(self) -> None:
        pass

    def get_dimensions(self) -> tuple[int, int]:
        return (self.width, self.height)


def _build_pipeline(
    items: list[SourceItem],
    history_depth: int = 5,
    width: int = 80,
    height: int = 24,
) -> tuple[Pipeline, QueueDisplay, PipelineContext]:
    """Build pipeline: source -> render -> framebuffer -> display."""
    display = QueueDisplay()

    ctx = PipelineContext()
    ctx.set("items", items)

    pipeline = Pipeline(
        config=PipelineConfig(enable_metrics=True),
        context=ctx,
    )

    # Source
    source = ListDataSource(items, name="test-source")
    pipeline.add_stage("source", DataSourceStage(source, name="test-source"))

    # Render
    pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))

    # Framebuffer
    framebuffer = FrameBufferStage(name="default", history_depth=history_depth)
    pipeline.add_stage("framebuffer", framebuffer)

    # Display
    pipeline.add_stage("display", DisplayStage(display, name="queue"))

    pipeline.build()
    pipeline.initialize()

    return pipeline, display, ctx


class TestFrameBufferAcceptance:
    """Test FrameBufferStage in a full pipeline."""

    def test_framebuffer_populates_history(self):
        """After several frames, framebuffer should have history stored."""
        items = [
            SourceItem(content="Frame\nBuffer\nTest", source="test", timestamp="0")
        ]
        pipeline, display, ctx = _build_pipeline(items, history_depth=5)

        # Run 3 frames
        for i in range(3):
            result = pipeline.execute([])
            assert result.success, f"Pipeline failed at frame {i}: {result.error}"

        # Check framebuffer history in context
        history = ctx.get("framebuffer.default.history")
        assert history is not None, "Framebuffer history not found in context"
        assert len(history) == 3, f"Expected 3 history frames, got {len(history)}"

    def test_framebuffer_respects_depth(self):
        """Framebuffer should not exceed configured history depth."""
        items = [SourceItem(content="Depth\nTest", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items, history_depth=3)

        # Run 5 frames
        for i in range(5):
            result = pipeline.execute([])
            assert result.success

        history = ctx.get("framebuffer.default.history")
        assert history is not None
        assert len(history) == 3, f"Expected depth 3, got {len(history)}"

    def test_framebuffer_current_intensity(self):
        """Framebuffer should compute current intensity map."""
        items = [SourceItem(content="Intensity\nMap", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items, history_depth=5)

        # Run at least one frame
        result = pipeline.execute([])
        assert result.success

        intensity = ctx.get("framebuffer.default.current_intensity")
        assert intensity is not None, "No intensity map in context"
        # Intensity should be a list of one value per line? Actually it's a 2D array or list?
        # Let's just check it's non-empty
        assert len(intensity) > 0, "Intensity map is empty"

    def test_framebuffer_get_frame(self):
        """Should be able to retrieve specific frames from history."""
        items = [SourceItem(content="Retrieve\nFrame", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items, history_depth=5)

        # Run 2 frames
        for i in range(2):
            result = pipeline.execute([])
            assert result.success

        # Retrieve frame 0 (most recent)
        recent = pipeline.get_stage("framebuffer").get_frame(0, ctx)
        assert recent is not None, "Cannot retrieve recent frame"
        assert len(recent) > 0, "Recent frame is empty"

        # Retrieve frame 1 (previous)
        previous = pipeline.get_stage("framebuffer").get_frame(1, ctx)
        assert previous is not None, "Cannot retrieve previous frame"

    def test_framebuffer_with_motionblur_effect(self):
        """MotionBlurEffect should work when depending on framebuffer."""
        from engine.effects.plugins.motionblur import MotionBlurEffect
        from engine.pipeline.adapters import EffectPluginStage

        items = [SourceItem(content="Motion\nBlur", source="test", timestamp="0")]
        display = QueueDisplay()
        ctx = PipelineContext()
        ctx.set("items", items)

        pipeline = Pipeline(
            config=PipelineConfig(enable_metrics=True),
            context=ctx,
        )

        source = ListDataSource(items, name="test")
        pipeline.add_stage("source", DataSourceStage(source, name="test"))
        pipeline.add_stage("render", SourceItemsToBufferStage(name="render"))

        framebuffer = FrameBufferStage(name="default", history_depth=3)
        pipeline.add_stage("framebuffer", framebuffer)

        motionblur = MotionBlurEffect()
        motionblur.configure(EffectConfig(enabled=True, intensity=0.5))
        pipeline.add_stage(
            "motionblur",
            EffectPluginStage(
                motionblur,
                name="motionblur",
                dependencies={"framebuffer.history.default"},
            ),
        )

        pipeline.add_stage("display", DisplayStage(display, name="queue"))

        pipeline.build()
        pipeline.initialize()

        # Run a few frames
        for i in range(5):
            result = pipeline.execute([])
            assert result.success, f"Motion blur pipeline failed at frame {i}"

        # Check that history exists
        history = ctx.get("framebuffer.default.history")
        assert history is not None
        assert len(history) > 0
