"""Adapter to convert Graph to Pipeline stages.

This module bridges the new graph-based abstraction with the existing
Stage-based pipeline system for backward compatibility.
"""

from typing import Dict, Any, List, Optional

from engine.pipeline.graph import Graph, NodeType
from engine.pipeline.controller import Pipeline, PipelineConfig
from engine.pipeline.core import PipelineContext
from engine.pipeline.params import PipelineParams
from engine.pipeline.adapters import (
    CameraStage,
    DataSourceStage,
    DisplayStage,
    EffectPluginStage,
    FontStage,
    MessageOverlayStage,
    PositionStage,
    ViewportFilterStage,
    create_stage_from_display,
    create_stage_from_effect,
)
from engine.pipeline.adapters.positioning import PositioningMode
from engine.display import DisplayRegistry
from engine.effects import get_registry
from engine.data_sources.sources import EmptyDataSource, HeadlinesDataSource
from engine.camera import Camera


class GraphAdapter:
    """Converts Graph to Pipeline with existing Stage classes."""

    def __init__(self, graph: Graph):
        self.graph = graph
        self.pipeline: Optional[Pipeline] = None
        self.context: Optional[PipelineContext] = None

    def build_pipeline(
        self, viewport_width: int = 80, viewport_height: int = 24
    ) -> Pipeline:
        """Build a Pipeline from the Graph."""
        # Create pipeline context
        self.context = PipelineContext()
        self.context.terminal_width = viewport_width
        self.context.terminal_height = viewport_height

        # Create params
        params = PipelineParams(
            viewport_width=viewport_width,
            viewport_height=viewport_height,
        )
        self.context.params = params

        # Create pipeline config
        config = PipelineConfig()

        # Create pipeline
        self.pipeline = Pipeline(config=config, context=self.context)

        # Map graph nodes to pipeline stages
        self._map_nodes_to_stages()

        # Build pipeline
        self.pipeline.build()

        return self.pipeline

    def _map_nodes_to_stages(self) -> None:
        """Map graph nodes to pipeline stages."""
        for name, node in self.graph.nodes.items():
            if not node.enabled:
                continue

            stage = self._create_stage_from_node(name, node)
            if stage:
                self.pipeline.add_stage(name, stage)

    def _create_stage_from_node(self, name: str, node) -> Optional:
        """Create a pipeline stage from a graph node."""
        stage = None

        if node.type == NodeType.SOURCE:
            source_type = node.config.get("source", "headlines")
            if source_type == "headlines":
                source = HeadlinesDataSource()
            elif source_type == "empty":
                source = EmptyDataSource(
                    width=self.context.terminal_width,
                    height=self.context.terminal_height,
                )
            else:
                source = EmptyDataSource(
                    width=self.context.terminal_width,
                    height=self.context.terminal_height,
                )
            stage = DataSourceStage(source, name=name)

        elif node.type == NodeType.CAMERA:
            mode = node.config.get("mode", "scroll")
            speed = node.config.get("speed", 1.0)
            # Map mode string to Camera factory method
            mode_lower = mode.lower()
            if hasattr(Camera, mode_lower):
                camera_factory = getattr(Camera, mode_lower)
                camera = camera_factory(speed=speed)
            else:
                # Fallback to scroll mode
                camera = Camera.scroll(speed=speed)
            stage = CameraStage(camera, name=name)

        elif node.type == NodeType.DISPLAY:
            backend = node.config.get("backend", "terminal")
            positioning = node.config.get("positioning", "mixed")
            display = DisplayRegistry.create(backend)
            if display:
                stage = DisplayStage(display, name=name, positioning=positioning)

        elif node.type == NodeType.EFFECT:
            effect_name = node.config.get("effect", "")
            intensity = node.config.get("intensity", 1.0)
            effect = get_registry().get(effect_name)
            if effect:
                # Set effect intensity (modifies global effect state)
                effect.config.intensity = intensity
                # Effects typically depend on rendered output
                dependencies = {"render.output"}
                stage = EffectPluginStage(effect, name=name, dependencies=dependencies)

        elif node.type == NodeType.RENDER:
            stage = FontStage(name=name)

        elif node.type == NodeType.OVERLAY:
            stage = MessageOverlayStage(name=name)

        elif node.type == NodeType.POSITION:
            mode_str = node.config.get("mode", "mixed")
            try:
                mode = PositioningMode(mode_str)
            except ValueError:
                mode = PositioningMode.MIXED
            stage = PositionStage(mode=mode, name=name)

        return stage


def graph_to_pipeline(
    graph: Graph, viewport_width: int = 80, viewport_height: int = 24
) -> Pipeline:
    """Convert a Graph to a Pipeline."""
    adapter = GraphAdapter(graph)
    return adapter.build_pipeline(viewport_width, viewport_height)


def dict_to_pipeline(
    data: Dict[str, Any], viewport_width: int = 80, viewport_height: int = 24
) -> Pipeline:
    """Convert a dictionary to a Pipeline."""
    graph = Graph().from_dict(data)
    return graph_to_pipeline(graph, viewport_width, viewport_height)
