"""
Base Pipeline abstract class and related types.

All pipelines must implement this interface to be compatible with the
pipeline registry, runner, and dynamic dashboard system.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any, TypedDict

from pipeline_core.contracts import DashboardConfig


class PipelineMetadata(TypedDict):
    """Metadata describing a pipeline."""

    id: str                    # Unique pipeline identifier (e.g., "reviewiq")
    name: str                  # Display name (e.g., "ReviewIQ Classification Pipeline")
    description: str           # Human-readable description
    version: str               # Semantic version (e.g., "1.0.0")
    stages: list[str]          # Ordered list of stage names
    input_type: str            # Expected input type (e.g., "ScraperV1Output")


class StageResult(TypedDict, total=False):
    """Result from running a single pipeline stage."""

    stage: str                 # Stage name
    success: bool              # Whether the stage succeeded
    data: dict[str, Any]       # Stage output data
    error: str | None          # Error message if failed
    duration_ms: int           # Stage execution time


class PipelineResult:
    """Result from running a pipeline."""

    def __init__(
        self,
        pipeline_id: str,
        stages_run: list[str] | None = None,
        stage_results: dict[str, StageResult] | None = None,
        success: bool = True,
        error: str | None = None,
    ):
        """
        Initialize pipeline result.

        Args:
            pipeline_id: Pipeline identifier
            stages_run: List of stages that were run
            stage_results: Results from each stage
            success: Overall success status
            error: Error message if failed
        """
        self.pipeline_id = pipeline_id
        self.stages_run = stages_run or []
        self.stage_results = stage_results or {}
        self.success = success
        self.error = error

    def get_stage_result(self, stage: str) -> StageResult | None:
        """Get result for a specific stage."""
        return self.stage_results.get(stage)

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "pipeline_id": self.pipeline_id,
            "stages_run": self.stages_run,
            "stage_results": self.stage_results,
            "success": self.success,
            "error": self.error,
        }

    @classmethod
    def from_error(cls, pipeline_id: str, error: str) -> PipelineResult:
        """Create a failed result from an error."""
        return cls(
            pipeline_id=pipeline_id,
            success=False,
            error=error,
        )


class BasePipeline(ABC):
    """
    Abstract base class for all pipelines.

    All pipelines must implement this interface to be compatible with:
    - Pipeline registry (discovery and management)
    - Pipeline runner (execution)
    - Dynamic dashboard system (widget configuration and data)

    Example implementation:

        class ReviewIQPipeline(BasePipeline):
            @property
            def metadata(self) -> PipelineMetadata:
                return {
                    "id": "reviewiq",
                    "name": "ReviewIQ Classification Pipeline",
                    "description": "Classifies reviews using URT taxonomy",
                    "version": "1.0.0",
                    "stages": ["normalize", "classify", "route", "aggregate"],
                    "input_type": "ScraperV1Output",
                }

            async def initialize(self) -> None:
                # Set up database connections, etc.
                pass

            async def process(self, input_data, stages=None) -> PipelineResult:
                # Run the pipeline
                pass

            def get_dashboard_config(self) -> DashboardConfig:
                return {
                    "pipeline_id": "reviewiq",
                    "title": "ReviewIQ Analytics",
                    "sections": [...]
                }

            async def get_widget_data(self, widget_id, params) -> dict:
                # Return data for a specific widget
                pass
    """

    @property
    @abstractmethod
    def metadata(self) -> PipelineMetadata:
        """
        Get pipeline metadata.

        Returns:
            PipelineMetadata with id, name, description, version, stages, input_type
        """
        ...

    @abstractmethod
    async def initialize(self) -> None:
        """
        Initialize the pipeline.

        This is called before any processing. Use it to:
        - Establish database connections
        - Load configuration
        - Initialize services

        This method may be called multiple times but should be idempotent.
        """
        ...

    @abstractmethod
    async def close(self) -> None:
        """
        Close and cleanup pipeline resources.

        This is called when the pipeline is no longer needed. Use it to:
        - Close database connections
        - Release resources
        - Cleanup temporary files
        """
        ...

    @abstractmethod
    async def process(
        self,
        input_data: dict[str, Any],
        stages: list[str] | None = None,
    ) -> PipelineResult:
        """
        Process input data through the pipeline.

        Args:
            input_data: Input data dictionary (format depends on input_type)
            stages: List of stages to run (default: all stages)

        Returns:
            PipelineResult with stage outputs and validation results
        """
        ...

    @abstractmethod
    def get_dashboard_config(self) -> DashboardConfig:
        """
        Get the dashboard configuration for this pipeline.

        Returns:
            DashboardConfig with sections and widget definitions

        The frontend uses this configuration to dynamically render
        the pipeline's dashboard with appropriate widgets.
        """
        ...

    @abstractmethod
    async def get_widget_data(
        self,
        widget_id: str,
        params: dict[str, Any],
    ) -> dict[str, Any]:
        """
        Get data for a specific dashboard widget.

        Args:
            widget_id: Widget identifier (from dashboard config)
            params: Query parameters (e.g., time range, filters)

        Returns:
            Dictionary with widget data in the format expected by the widget type

        Common params:
            - business_id: Filter by business
            - time_range: Time range (e.g., "7d", "30d", "custom")
            - start_date: Start date for custom range
            - end_date: End date for custom range
        """
        ...

    # Optional methods with default implementations

    async def validate_input(self, input_data: dict[str, Any]) -> list[str]:
        """
        Validate input data before processing.

        Args:
            input_data: Input data to validate

        Returns:
            List of validation error messages (empty if valid)

        Override this to add custom input validation.
        """
        return []

    async def health_check(self) -> dict[str, Any]:
        """
        Perform a health check on the pipeline.

        Returns:
            Dictionary with health status:
                - healthy: bool
                - checks: dict of individual check results
                - message: optional message

        Override this to add custom health checks.
        """
        return {
            "healthy": True,
            "checks": {},
            "message": None,
        }

    def get_stage_names(self) -> list[str]:
        """Get the list of stage names."""
        return self.metadata["stages"]

    def get_pipeline_id(self) -> str:
        """Get the pipeline identifier."""
        return self.metadata["id"]
