"""
Acceptance tests for HUD visibility and positioning.

These tests verify that HUD appears in the final output frame.
Frames are captured and saved as HTML reports for visual verification.
"""

import queue

from engine.data_sources.sources import ListDataSource, SourceItem
from engine.effects.plugins.hud import HudEffect
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
from tests.acceptance_report import save_report


class FrameCaptureDisplay:
    """Display that captures frames for HTML report generation."""

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

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

    def show(self, buffer: list[str], border: bool = False) -> None:
        self._recorded_frames.append(list(buffer))
        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 get_recorded_frames(self) -> list[list[str]]:
        return self._recorded_frames


def _build_pipeline_with_hud(
    items: list[SourceItem],
) -> tuple[Pipeline, FrameCaptureDisplay, PipelineContext]:
    """Build a pipeline with HUD effect."""
    display = FrameCaptureDisplay()

    ctx = PipelineContext()
    params = PipelineParams()
    params.viewport_width = display.width
    params.viewport_height = display.height
    params.frame_number = 0
    params.effect_order = ["noise", "hud"]
    params.effect_enabled = {"noise": False}
    ctx.params = params

    pipeline = Pipeline(
        config=PipelineConfig(
            source="list",
            display="terminal",
            effects=["hud"],
            enable_metrics=True,
        ),
        context=ctx,
    )

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

    hud_effect = HudEffect()
    pipeline.add_stage("hud", EffectPluginStage(hud_effect, name="hud"))

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

    pipeline.build()
    pipeline.initialize()

    return pipeline, display, ctx


class TestHUDAcceptance:
    """Acceptance tests for HUD visibility."""

    def test_hud_appears_in_final_output(self):
        """Test that HUD appears in the final display output.

        This is the key regression test for Issue #47 - HUD was running
        AFTER the display stage, making it invisible. Now it should appear
        in the frame captured by the display.
        """
        items = [SourceItem(content="Test content line", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline_with_hud(items)

        result = pipeline.execute(items)
        assert result.success, f"Pipeline execution failed: {result.error}"

        frame = display.frames.get(timeout=1)
        frame_text = "\n".join(frame)

        assert "MAINLINE" in frame_text, "HUD header not found in final output"
        assert "EFFECT:" in frame_text, "EFFECT line not found in final output"
        assert "PIPELINE:" in frame_text, "PIPELINE line not found in final output"

        save_report(
            test_name="test_hud_appears_in_final_output",
            frames=display.get_recorded_frames(),
            status="PASS",
            metadata={
                "description": "Verifies HUD appears in final display output (Issue #47 fix)",
                "frame_lines": len(frame),
                "has_mainline": "MAINLINE" in frame_text,
                "has_effect": "EFFECT:" in frame_text,
                "has_pipeline": "PIPELINE:" in frame_text,
            },
        )

    def test_hud_cursor_positioning(self):
        """Test that HUD uses correct cursor positioning."""
        items = [SourceItem(content="Sample content", source="test", timestamp="0")]
        pipeline, display, ctx = _build_pipeline_with_hud(items)

        result = pipeline.execute(items)
        assert result.success

        frame = display.frames.get(timeout=1)
        has_cursor_pos = any("\x1b[" in line and "H" in line for line in frame)

        save_report(
            test_name="test_hud_cursor_positioning",
            frames=display.get_recorded_frames(),
            status="PASS",
            metadata={
                "description": "Verifies HUD uses cursor positioning",
                "has_cursor_positioning": has_cursor_pos,
            },
        )


class TestCameraSpeedAcceptance:
    """Acceptance tests for camera speed modulation."""

    def test_camera_speed_modulation(self):
        """Test that camera speed can be modulated at runtime.

        This verifies the camera speed modulation feature added in Phase 1.
        """
        from engine.camera import Camera
        from engine.pipeline.adapters import CameraClockStage, CameraStage

        display = FrameCaptureDisplay()
        items = [
            SourceItem(content=f"Line {i}", source="test", timestamp=str(i))
            for i in range(50)
        ]

        ctx = PipelineContext()
        params = PipelineParams()
        params.viewport_width = display.width
        params.viewport_height = display.height
        params.frame_number = 0
        params.camera_speed = 1.0
        ctx.params = params

        pipeline = Pipeline(
            config=PipelineConfig(
                source="list",
                display="terminal",
                camera="scroll",
                enable_metrics=False,
            ),
            context=ctx,
        )

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

        camera = Camera.scroll(speed=0.5)
        pipeline.add_stage(
            "camera_update", CameraClockStage(camera, name="camera-clock")
        )
        pipeline.add_stage("camera", CameraStage(camera, name="camera"))
        pipeline.add_stage("display", DisplayStage(display, name="terminal"))

        pipeline.build()
        pipeline.initialize()

        initial_camera_speed = camera.speed

        for _ in range(3):
            pipeline.execute(items)

        speed_after_first_run = camera.speed

        params.camera_speed = 5.0
        ctx.params = params

        for _ in range(3):
            pipeline.execute(items)

        speed_after_increase = camera.speed

        assert speed_after_increase == 5.0, (
            f"Camera speed should be modulated to 5.0, got {speed_after_increase}"
        )

        params.camera_speed = 0.0
        ctx.params = params

        for _ in range(3):
            pipeline.execute(items)

        speed_after_stop = camera.speed
        assert speed_after_stop == 0.0, (
            f"Camera speed should be 0.0, got {speed_after_stop}"
        )

        save_report(
            test_name="test_camera_speed_modulation",
            frames=display.get_recorded_frames()[:5],
            status="PASS",
            metadata={
                "description": "Verifies camera speed can be modulated at runtime",
                "initial_camera_speed": initial_camera_speed,
                "speed_after_first_run": speed_after_first_run,
                "speed_after_increase": speed_after_increase,
                "speed_after_stop": speed_after_stop,
            },
        )


class TestEmptyLinesAcceptance:
    """Acceptance tests for empty line handling."""

    def test_empty_lines_remain_empty(self):
        """Test that empty lines remain empty in output (regression for padding bug)."""
        items = [
            SourceItem(content="Line1\n\nLine3\n\nLine5", source="test", timestamp="0")
        ]

        display = FrameCaptureDisplay()
        ctx = PipelineContext()
        params = PipelineParams()
        params.viewport_width = display.width
        params.viewport_height = display.height
        ctx.params = params

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

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

        pipeline.build()
        pipeline.initialize()

        result = pipeline.execute(items)
        assert result.success

        frame = display.frames.get(timeout=1)
        has_truly_empty = any(not line for line in frame)

        save_report(
            test_name="test_empty_lines_remain_empty",
            frames=display.get_recorded_frames(),
            status="PASS",
            metadata={
                "description": "Verifies empty lines remain empty (not padded)",
                "has_truly_empty_lines": has_truly_empty,
            },
        )

        assert has_truly_empty, f"Expected at least one empty line, got: {frame[1]!r}"
