"""
Visual verification tests for message overlay and effect rendering.

These tests verify that the sideline pipeline produces visual output
that matches the expected behavior of upstream/main, even if the
buffer format differs due to architectural differences.
"""

import json
from pathlib import Path

import pytest

from engine.display import DisplayRegistry
from engine.pipeline import Pipeline, PipelineConfig, PipelineContext
from engine.pipeline.adapters import create_stage_from_display
from engine.pipeline.params import PipelineParams
from engine.pipeline.presets import get_preset


class TestMessageOverlayVisuals:
    """Test message overlay visual rendering."""

    def test_message_overlay_produces_output(self):
        """Verify message overlay stage produces output when ntfy message is present."""
        # This test verifies the message overlay stage is working
        # It doesn't compare with upstream, just verifies functionality

        from engine.pipeline.adapters.message_overlay import MessageOverlayStage
        from engine.pipeline.adapters import MessageOverlayConfig

        # Test the rendering function directly
        stage = MessageOverlayStage(
            config=MessageOverlayConfig(enabled=True, display_secs=30)
        )

        # Test with a mock message
        msg = ("Test Title", "Test Message Body", 0.0)
        w, h = 80, 24

        # Render overlay
        overlay, _ = stage._render_message_overlay(msg, w, h, (None, None))

        # Verify overlay has content
        assert len(overlay) > 0, "Overlay should have content when message is present"

        # Verify overlay contains expected content
        overlay_text = "".join(overlay)
        # Note: Message body is rendered as block characters, not text
        # The title appears in the metadata line
        assert "Test Title" in overlay_text, "Overlay should contain message title"
        assert "ntfy" in overlay_text, "Overlay should contain ntfy metadata"
        assert "\033[" in overlay_text, "Overlay should contain ANSI codes"

    def test_message_overlay_appears_in_correct_position(self):
        """Verify message overlay appears in centered position."""
        # This test verifies the message overlay positioning logic
        # It checks that the overlay coordinates are calculated correctly

        from engine.pipeline.adapters.message_overlay import MessageOverlayStage
        from engine.pipeline.adapters import MessageOverlayConfig

        stage = MessageOverlayStage(config=MessageOverlayConfig())

        # Test positioning calculation
        msg = ("Test Title", "Test Body", 0.0)
        w, h = 80, 24

        # Render overlay
        overlay, _ = stage._render_message_overlay(msg, w, h, (None, None))

        # Verify overlay has content
        assert len(overlay) > 0, "Overlay should have content"

        # Verify overlay contains cursor positioning codes
        overlay_text = "".join(overlay)
        assert "\033[" in overlay_text, "Overlay should contain ANSI codes"
        assert "H" in overlay_text, "Overlay should contain cursor positioning"

        # Verify panel is centered (check first line's position)
        # Panel height is len(msg_rows) + 2 (content + meta + border)
        # panel_top = max(0, (h - panel_h) // 2)
        # First content line should be at panel_top + 1
        first_line = overlay[0]
        assert "\033[" in first_line, "First line should have cursor positioning"
        assert ";1H" in first_line, "First line should position at column 1"

    def test_theme_system_integration(self):
        """Verify theme system is integrated with message overlay."""
        from engine import config as engine_config
        from engine.themes import THEME_REGISTRY

        # Verify theme registry has expected themes
        assert "green" in THEME_REGISTRY, "Green theme should exist"
        assert "orange" in THEME_REGISTRY, "Orange theme should exist"
        assert "purple" in THEME_REGISTRY, "Purple theme should exist"

        # Verify active theme is set
        assert engine_config.ACTIVE_THEME is not None, "Active theme should be set"
        assert engine_config.ACTIVE_THEME.name in THEME_REGISTRY, (
            "Active theme should be in registry"
        )

        # Verify theme has gradient colors
        assert len(engine_config.ACTIVE_THEME.main_gradient) == 12, (
            "Main gradient should have 12 colors"
        )
        assert len(engine_config.ACTIVE_THEME.message_gradient) == 12, (
            "Message gradient should have 12 colors"
        )


class TestPipelineExecutionOrder:
    """Test pipeline execution order for visual consistency."""

    def test_message_overlay_after_camera(self):
        """Verify message overlay is applied after camera transformation."""
        from engine.pipeline import Pipeline, PipelineConfig, PipelineContext
        from engine.pipeline.adapters import (
            create_stage_from_display,
            MessageOverlayStage,
            MessageOverlayConfig,
        )
        from engine.display import DisplayRegistry

        # Create pipeline
        config = PipelineConfig(
            source="empty",
            display="null",
            camera="feed",
            effects=[],
        )

        ctx = PipelineContext()
        pipeline = Pipeline(config=config, context=ctx)

        # Add stages
        from engine.data_sources.sources import EmptyDataSource
        from engine.pipeline.adapters import DataSourceStage

        pipeline.add_stage(
            "source",
            DataSourceStage(EmptyDataSource(width=80, height=24), name="empty"),
        )
        pipeline.add_stage(
            "message_overlay", MessageOverlayStage(config=MessageOverlayConfig())
        )
        pipeline.add_stage(
            "display", create_stage_from_display(DisplayRegistry.create("null"), "null")
        )

        # Build and check order
        pipeline.build()
        execution_order = pipeline.execution_order

        # Verify message_overlay comes after camera stages
        camera_idx = next(
            (i for i, name in enumerate(execution_order) if "camera" in name), -1
        )
        msg_idx = next(
            (i for i, name in enumerate(execution_order) if "message_overlay" in name),
            -1,
        )

        if camera_idx >= 0 and msg_idx >= 0:
            assert msg_idx > camera_idx, "Message overlay should be after camera stage"


class TestCapturedOutputAnalysis:
    """Test analysis of captured output files."""

    def test_captured_files_exist(self):
        """Verify captured output files exist."""
        sideline_path = Path("output/sideline_demo.json")
        upstream_path = Path("output/upstream_demo.json")

        assert sideline_path.exists(), "Sideline capture file should exist"
        assert upstream_path.exists(), "Upstream capture file should exist"

    def test_captured_files_valid(self):
        """Verify captured output files are valid JSON."""
        sideline_path = Path("output/sideline_demo.json")
        upstream_path = Path("output/upstream_demo.json")

        with open(sideline_path) as f:
            sideline = json.load(f)
        with open(upstream_path) as f:
            upstream = json.load(f)

        # Verify structure
        assert "frames" in sideline, "Sideline should have frames"
        assert "frames" in upstream, "Upstream should have frames"
        assert len(sideline["frames"]) > 0, "Sideline should have at least one frame"
        assert len(upstream["frames"]) > 0, "Upstream should have at least one frame"

    def test_sideline_buffer_format(self):
        """Verify sideline buffer format is plain text."""
        sideline_path = Path("output/sideline_demo.json")

        with open(sideline_path) as f:
            sideline = json.load(f)

        # Check first frame
        frame0 = sideline["frames"][0]["buffer"]

        # Sideline should have plain text lines (no cursor positioning)
        # Check first few lines
        for i, line in enumerate(frame0[:5]):
            # Should not start with cursor positioning
            if line.strip():
                assert not line.startswith("\033["), (
                    f"Line {i} should not start with cursor positioning"
                )
                # Should have actual content
                assert len(line.strip()) > 0, f"Line {i} should have content"

    def test_upstream_buffer_format(self):
        """Verify upstream buffer format includes cursor positioning."""
        upstream_path = Path("output/upstream_demo.json")

        with open(upstream_path) as f:
            upstream = json.load(f)

        # Check first frame
        frame0 = upstream["frames"][0]["buffer"]

        # Upstream should have cursor positioning codes
        overlay_text = "".join(frame0[:10])
        assert "\033[" in overlay_text, "Upstream buffer should contain ANSI codes"
        assert "H" in overlay_text, "Upstream buffer should contain cursor positioning"


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
