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

import queue

from engine.data_sources.sources import ListDataSource, SourceItem
from engine.effects.plugins.tint import TintEffect
from engine.effects.types import EffectConfig
from engine.pipeline import Pipeline, PipelineConfig
from engine.pipeline.adapters import (
    DataSourceStage,
    DisplayStage,
    EffectPluginStage,
    SourceItemsToBufferStage,
)
from engine.pipeline.core import PipelineContext
from engine.pipeline.params import PipelineParams


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],
    tint_config: EffectConfig | None = None,
    width: int = 80,
    height: int = 24,
) -> tuple[Pipeline, QueueDisplay, PipelineContext]:
    """Build pipeline: source -> render -> tint effect -> display."""
    display = QueueDisplay()

    ctx = PipelineContext()
    params = PipelineParams()
    params.viewport_width = width
    params.viewport_height = height
    params.frame_number = 0
    ctx.params = params
    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 (simple)
    pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))

    # Tint effect
    tint_effect = TintEffect()
    if tint_config is not None:
        tint_effect.configure(tint_config)
    pipeline.add_stage("tint", EffectPluginStage(tint_effect, name="tint"))

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

    pipeline.build()
    pipeline.initialize()

    return pipeline, display, ctx


class TestTintAcceptance:
    """Test TintEffect in a full pipeline."""

    def test_tint_applies_default_color(self):
        """Default tint should apply ANSI color codes to output."""
        items = [SourceItem(content="Hello World", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items)

        result = pipeline.execute(items)

        assert result.success, f"Pipeline failed: {result.error}"
        frame = display.frames.get(timeout=1)

        text = "\n".join(frame)
        assert "\033[" in text, f"Expected ANSI codes in frame: {frame}"
        assert "Hello World" in text

    def test_tint_applies_red_color(self):
        """Configured red tint should produce red ANSI code (196-197)."""
        items = [SourceItem(content="Red Text", source="test", timestamp="0")]
        config = EffectConfig(
            enabled=True,
            intensity=1.0,
            params={"r": 255, "g": 0, "b": 0, "a": 0.8},
        )
        pipeline, display, ctx = _build_pipeline(items, tint_config=config)

        result = pipeline.execute(items)

        assert result.success
        frame = display.frames.get(timeout=1)
        line = frame[0]

        # Should contain red ANSI code (196 or 197 in 256 color)
        assert "\033[38;5;196m" in line or "\033[38;5;197m" in line, (
            f"Missing red tint: {line}"
        )
        assert "Red Text" in line

    def test_tint_disabled_does_nothing(self):
        """Disabled tint stage should pass through buffer unchanged."""
        items = [SourceItem(content="Plain Text", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items)

        # Disable the tint stage
        stage = pipeline.get_stage("tint")
        stage.set_enabled(False)

        result = pipeline.execute(items)

        assert result.success
        frame = display.frames.get(timeout=1)
        text = "\n".join(frame)

        # Should contain Plain Text with NO ANSI color codes
        assert "Plain Text" in text
        assert "\033[" not in text, f"Unexpected ANSI codes in frame: {frame}"

    def test_tint_zero_transparency(self):
        """Alpha=0 should pass through buffer unchanged (no tint)."""
        items = [SourceItem(content="Transparent", source="test", timestamp="0")]
        config = EffectConfig(
            enabled=True,
            intensity=1.0,
            params={"r": 255, "g": 128, "b": 64, "a": 0.0},
        )
        pipeline, display, ctx = _build_pipeline(items, tint_config=config)

        result = pipeline.execute(items)

        assert result.success
        frame = display.frames.get(timeout=1)
        text = "\n".join(frame)

        assert "Transparent" in text
        assert "\033[" not in text, f"Expected no ANSI codes with alpha=0: {frame}"

    def test_tint_with_multiples_lines(self):
        """Tint should apply to all non-empty lines."""
        items = [
            SourceItem(content="Line1\nLine2\n\nLine4", source="test", timestamp="0")
        ]
        config = EffectConfig(
            enabled=True,
            intensity=1.0,
            params={"r": 0, "g": 255, "b": 0, "a": 0.7},
        )
        pipeline, display, ctx = _build_pipeline(items, tint_config=config)

        result = pipeline.execute(items)

        assert result.success
        frame = display.frames.get(timeout=1)

        # All non-empty lines should have green ANSI codes
        green_codes = ["\033[38;5;", "m"]
        for line in frame:
            if line.strip():
                assert green_codes[0] in line and green_codes[1] in line, (
                    f"Missing green tint: {line}"
                )
            else:
                assert line == "", f"Empty lines should be exactly empty: {line}"

    def test_tint_preserves_empty_lines(self):
        """Empty lines should remain empty (no ANSI codes)."""
        items = [SourceItem(content="A\n\nB", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline(items)

        result = pipeline.execute(items)

        assert result.success
        frame = display.frames.get(timeout=1)

        assert frame[0].strip() != ""
        assert frame[1] == ""  # Empty line unchanged
        assert frame[2].strip() != ""
