"""Tests for pipeline execution order verification."""

from unittest.mock import MagicMock

import pytest

from engine.pipeline import Pipeline, Stage, discover_stages
from engine.pipeline.core import DataType


@pytest.fixture(autouse=True)
def reset_registry():
    """Reset stage registry before each test."""
    from engine.pipeline.registry import StageRegistry

    StageRegistry._discovered = False
    StageRegistry._categories.clear()
    StageRegistry._instances.clear()
    discover_stages()
    yield
    StageRegistry._discovered = False
    StageRegistry._categories.clear()
    StageRegistry._instances.clear()


def _create_mock_stage(name: str, category: str, capabilities: set, dependencies: set):
    """Helper to create a mock stage."""
    mock = MagicMock(spec=Stage)
    mock.name = name
    mock.category = category
    mock.stage_type = category
    mock.render_order = 0
    mock.is_overlay = False
    mock.inlet_types = {DataType.ANY}
    mock.outlet_types = {DataType.TEXT_BUFFER}
    mock.capabilities = capabilities
    mock.dependencies = dependencies
    mock.process = lambda data, ctx: data
    mock.init = MagicMock(return_value=True)
    mock.cleanup = MagicMock()
    mock.is_enabled = MagicMock(return_value=True)
    mock.set_enabled = MagicMock()
    mock._enabled = True
    return mock


def test_pipeline_execution_order_linear():
    """Verify stages execute in linear order based on dependencies."""
    pipeline = Pipeline()
    pipeline.build(auto_inject=False)

    source = _create_mock_stage("source", "source", {"source"}, set())
    render = _create_mock_stage("render", "render", {"render"}, {"source"})
    effect = _create_mock_stage("effect", "effect", {"effect"}, {"render"})
    display = _create_mock_stage("display", "display", {"display"}, {"effect"})

    pipeline.add_stage("source", source, initialize=False)
    pipeline.add_stage("render", render, initialize=False)
    pipeline.add_stage("effect", effect, initialize=False)
    pipeline.add_stage("display", display, initialize=False)

    pipeline._rebuild()

    assert pipeline.execution_order == [
        "source",
        "render",
        "effect",
        "display",
    ]


def test_pipeline_effects_chain_order():
    """Verify effects execute in config order when chained."""
    pipeline = Pipeline()
    pipeline.build(auto_inject=False)

    # Source and render
    source = _create_mock_stage("source", "source", {"source"}, set())
    render = _create_mock_stage("render", "render", {"render"}, {"source"})

    # Effects chain: effect_a → effect_b → effect_c
    effect_a = _create_mock_stage("effect_a", "effect", {"effect_a"}, {"render"})
    effect_b = _create_mock_stage("effect_b", "effect", {"effect_b"}, {"effect_a"})
    effect_c = _create_mock_stage("effect_c", "effect", {"effect_c"}, {"effect_b"})

    # Display
    display = _create_mock_stage("display", "display", {"display"}, {"effect_c"})

    for stage in [source, render, effect_a, effect_b, effect_c, display]:
        pipeline.add_stage(stage.name, stage, initialize=False)

    pipeline._rebuild()

    effect_names = [
        name for name in pipeline.execution_order if name.startswith("effect_")
    ]
    assert effect_names == ["effect_a", "effect_b", "effect_c"]


def test_pipeline_overlay_executes_after_regular_effects():
    """Overlay stages should execute after all regular effects."""
    pipeline = Pipeline()
    pipeline.build(auto_inject=False)

    effect = _create_mock_stage("effect1", "effect", {"effect1"}, {"render"})
    overlay = _create_mock_stage("overlay_test", "overlay", {"overlay"}, {"effect1"})
    display = _create_mock_stage("display", "display", {"display"}, {"overlay"})

    for stage in [effect, overlay, display]:
        pipeline.add_stage(stage.name, stage, initialize=False)

    pipeline._rebuild()

    names = pipeline.execution_order
    idx_effect = names.index("effect1")
    idx_overlay = names.index("overlay_test")
    idx_display = names.index("display")
    assert idx_effect < idx_overlay < idx_display
