"""Unit tests for pipeline state management."""

from __future__ import annotations

from pathlib import Path

import pytest

from cascade_fm.pipeline import Pipeline
from cascade_fm.types import PaneStatus


class TestPipeline:
    """Test Pipeline class."""

    def test_create_empty_pipeline(self) -> None:
        """Test creating an empty pipeline."""
        pipeline = Pipeline()
        assert pipeline.panes == []

    def test_add_pane(self) -> None:
        """Test adding a pane to the pipeline."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension", {"extensions": [".jpg"]})

        assert pane_id == "pane_0"
        assert len(pipeline.panes) == 1
        assert pipeline.panes[0].id == "pane_0"
        assert pipeline.panes[0].operation.name == "filter_extension"
        assert pipeline.panes[0].operation.params == {"extensions": [".jpg"]}
        assert pipeline.panes[0].status == PaneStatus.CONFIGURED

    def test_add_multiple_panes(self) -> None:
        """Test adding multiple panes."""
        pipeline = Pipeline()
        pane_id1 = pipeline.add_pane("filter_extension", {"extensions": [".jpg"]})
        pane_id2 = pipeline.add_pane("filter_name", {"pattern": "test*"})

        assert pane_id1 == "pane_0"
        assert pane_id2 == "pane_1"
        assert len(pipeline.panes) == 2

    def test_get_pane(self) -> None:
        """Test retrieving a pane by ID."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension")

        pane = pipeline.get_pane(pane_id)
        assert pane.id == pane_id

    def test_get_pane_not_found(self) -> None:
        """Test getting a non-existent pane raises error."""
        pipeline = Pipeline()

        with pytest.raises(ValueError, match="Pane not found"):
            pipeline.get_pane("nonexistent")

    def test_set_pane_params(self) -> None:
        """Test setting pane parameters."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension", {"extensions": [".jpg"]})

        pipeline.set_pane_params(pane_id, {"extensions": [".png"]})

        pane = pipeline.get_pane(pane_id)
        assert pane.operation.params == {"extensions": [".png"]}

    def test_update_browser_files_propagates_to_next_pane(self, tmp_path: Path) -> None:
        """Test updating browser files propagates to first operation pane."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension")

        file_jpg = tmp_path / "test.jpg"
        file_txt = tmp_path / "test.txt"
        file_jpg.write_text("jpg")
        file_txt.write_text("txt")

        pipeline.set_pane_params(pane_id, {"extensions": [".jpg"]})
        pipeline.update_browser_files([file_jpg, file_txt])

        pane = pipeline.get_pane(pane_id)
        assert pane.status == PaneStatus.DONE
        assert pane.output_files == [file_jpg]

    def test_switch_operation_at_bar_replaces_downstream(self) -> None:
        """Test switching operation at bar replaces that pane and downstream panes."""
        pipeline = Pipeline()
        pipeline.add_pane("filter_extension")
        pipeline.add_pane("filter_name")

        assert len(pipeline.panes) == 2

        new_pane_id = pipeline.switch_operation_at_bar(0, "filter_name")

        assert new_pane_id == "pane_1"
        assert len(pipeline.panes) == 2
        assert pipeline.panes[1].operation.name == "filter_name"

    def test_on_pane_updated_callback_is_called(self, tmp_path: Path) -> None:
        """Test UI callback is called when pane output changes."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension")

        updated: list[str] = []
        pipeline.on_pane_updated = lambda updated_pane_id: updated.append(updated_pane_id)

        file_jpg = tmp_path / "test.jpg"
        file_jpg.write_text("jpg")

        pipeline.set_pane_params(pane_id, {"extensions": [".jpg"]})
        pipeline.update_browser_files([file_jpg])

        assert pane_id in updated

    def test_remove_pane(self) -> None:
        """Test removing a pane."""
        pipeline = Pipeline()
        pane_id1 = pipeline.add_pane("filter_extension")
        pane_id2 = pipeline.add_pane("filter_name")
        pipeline.add_pane("filter_extension")

        assert len(pipeline.panes) == 3

        # Remove middle pane should remove it and all downstream panes
        pipeline.remove_pane(pane_id2)

        assert len(pipeline.panes) == 1
        assert pipeline.panes[0].id == pane_id1

    def test_get_previous_pane(self) -> None:
        """Test getting the previous pane."""
        pipeline = Pipeline()
        pane_id1 = pipeline.add_pane("filter_extension")
        pane_id2 = pipeline.add_pane("filter_name")

        prev = pipeline.get_previous_pane(pane_id2)
        assert prev is not None
        assert prev.id == pane_id1

    def test_get_previous_pane_first_pane(self) -> None:
        """Test getting previous pane for the first pane returns None."""
        pipeline = Pipeline()
        pane_id = pipeline.add_pane("filter_extension")

        prev = pipeline.get_previous_pane(pane_id)
        assert prev is None
