"""
Pipeline validation and MVP (Minimum Viable Pipeline) injection.

Provides validation functions to ensure pipelines meet minimum requirements
and can auto-inject sensible defaults when fields are missing or invalid.
"""

from dataclasses import dataclass
from typing import Any

from engine.display import BorderMode, DisplayRegistry
from engine.effects import get_registry
from engine.pipeline.params import PipelineParams

# Known valid values
VALID_SOURCES = ["headlines", "poetry", "fixture", "empty", "pipeline-inspect"]
VALID_CAMERAS = [
    "feed",
    "scroll",
    "vertical",
    "horizontal",
    "omni",
    "floating",
    "bounce",
    "radial",
    "static",
    "none",
    "",
]
VALID_DISPLAYS = None  # Will be populated at runtime from DisplayRegistry


@dataclass
class ValidationResult:
    """Result of validation with changes and warnings."""

    valid: bool
    warnings: list[str]
    changes: list[str]
    config: Any  # PipelineConfig (forward ref)
    params: PipelineParams


# MVP defaults
MVP_DEFAULTS = {
    "source": "fixture",
    "display": "terminal",
    "camera": "static",  # Static camera provides camera_y=0 for viewport filtering
    "effects": [],
    "border": False,
}


def validate_pipeline_config(
    config: Any, params: PipelineParams, allow_unsafe: bool = False
) -> ValidationResult:
    """Validate pipeline configuration against MVP requirements.

    Args:
        config: PipelineConfig object (has source, display, camera, effects fields)
        params: PipelineParams object (has border field)
        allow_unsafe: If True, don't inject defaults or enforce MVP

    Returns:
        ValidationResult with validity, warnings, changes, and validated config/params
    """
    warnings = []
    changes = []

    if allow_unsafe:
        # Still do basic validation but don't inject defaults
        # Always return valid=True when allow_unsafe is set
        warnings.extend(_validate_source(config.source))
        warnings.extend(_validate_display(config.display))
        warnings.extend(_validate_camera(config.camera))
        warnings.extend(_validate_effects(config.effects))
        warnings.extend(_validate_border(params.border))
        return ValidationResult(
            valid=True,  # Always valid with allow_unsafe
            warnings=warnings,
            changes=[],
            config=config,
            params=params,
        )

    # MVP injection mode
    # Source
    source_issues = _validate_source(config.source)
    if source_issues:
        warnings.extend(source_issues)
        config.source = MVP_DEFAULTS["source"]
        changes.append(f"source → {MVP_DEFAULTS['source']}")

    # Display
    display_issues = _validate_display(config.display)
    if display_issues:
        warnings.extend(display_issues)
        config.display = MVP_DEFAULTS["display"]
        changes.append(f"display → {MVP_DEFAULTS['display']}")

    # Camera
    camera_issues = _validate_camera(config.camera)
    if camera_issues:
        warnings.extend(camera_issues)
        config.camera = MVP_DEFAULTS["camera"]
        changes.append("camera → static (no camera stage)")

    # Effects
    effect_issues = _validate_effects(config.effects)
    if effect_issues:
        warnings.extend(effect_issues)
        # Only change if all effects are invalid
        if len(config.effects) == 0 or all(
            e not in _get_valid_effects() for e in config.effects
        ):
            config.effects = MVP_DEFAULTS["effects"]
            changes.append("effects → [] (none)")
        else:
            # Remove invalid effects, keep valid ones
            valid_effects = [e for e in config.effects if e in _get_valid_effects()]
            if valid_effects != config.effects:
                config.effects = valid_effects
                changes.append(f"effects → {valid_effects}")

    # Border (in params)
    border_issues = _validate_border(params.border)
    if border_issues:
        warnings.extend(border_issues)
        params.border = MVP_DEFAULTS["border"]
        changes.append(f"border → {MVP_DEFAULTS['border']}")

    valid = len(warnings) == 0
    if changes:
        # If we made changes, pipeline should be valid now
        valid = True

    return ValidationResult(
        valid=valid,
        warnings=warnings,
        changes=changes,
        config=config,
        params=params,
    )


def _validate_source(source: str) -> list[str]:
    """Validate source field."""
    if not source:
        return ["source is empty"]
    if source not in VALID_SOURCES:
        return [f"unknown source '{source}', valid sources: {VALID_SOURCES}"]
    return []


def _validate_display(display: str) -> list[str]:
    """Validate display field."""
    if not display:
        return ["display is empty"]
    # Check if display is available (lazy load registry)
    try:
        available = DisplayRegistry.list_backends()
        if display not in available:
            return [f"display '{display}' not available, available: {available}"]
    except Exception as e:
        return [f"error checking display availability: {e}"]
    return []


def _validate_camera(camera: str | None) -> list[str]:
    """Validate camera field."""
    if camera is None:
        return ["camera is None"]
    # Empty string is valid (static, no camera stage)
    if camera == "":
        return []
    if camera not in VALID_CAMERAS:
        return [f"unknown camera '{camera}', valid cameras: {VALID_CAMERAS}"]
    return []


def _get_valid_effects() -> set[str]:
    """Get set of valid effect names."""
    registry = get_registry()
    return set(registry.list_all().keys())


def _validate_effects(effects: list[str]) -> list[str]:
    """Validate effects list."""
    if effects is None:
        return ["effects is None"]
    valid_effects = _get_valid_effects()
    issues = []
    for effect in effects:
        if effect not in valid_effects:
            issues.append(
                f"unknown effect '{effect}', valid effects: {sorted(valid_effects)}"
            )
    return issues


def _validate_border(border: bool | BorderMode) -> list[str]:
    """Validate border field."""
    if isinstance(border, bool):
        return []
    if isinstance(border, BorderMode):
        return []
    return [f"invalid border value, must be bool or BorderMode, got {type(border)}"]


def get_mvp_summary(config: Any, params: PipelineParams) -> str:
    """Get a human-readable summary of the MVP pipeline configuration."""
    camera_text = "none" if not config.camera else config.camera
    effects_text = "none" if not config.effects else ", ".join(config.effects)
    return (
        f"MVP Pipeline Configuration:\n"
        f"  Source: {config.source}\n"
        f"  Display: {config.display}\n"
        f"  Camera: {camera_text} (static if empty)\n"
        f"  Effects: {effects_text}\n"
        f"  Border: {params.border}"
    )
