"""ContextValidator - Validate project_context.yaml.

Contract: CNT-T2-SVC-006
Traceability: BR-011, BR-012
"""

from pathlib import Path
from typing import Any

from laida_cli.adapters.yaml_adapter import YamlAdapter
from laida_cli.domain.validation_result import ValidationResult
from laida_cli.ports.yaml_port import YamlPort


class ContextValidator:
    """Application service for validating project_context.yaml.

    Validates:
    - Required fields are present
    - Business rule constraints (BR-011: criticality requires budget)
    - Data types and values
    """

    REQUIRED_FIELDS = [
        "project_name",
        "description",
    ]

    OPTIONAL_FIELDS = [
        "criticality",
        "budget",
        "timeline",
        "team_size",
        "constraints",
        "integrations",
    ]

    def __init__(self, yaml_port: YamlPort | None = None) -> None:
        """Initialize context validator.

        Args:
            yaml_port: Port for YAML operations (uses default adapter if None)
        """
        self._yaml: YamlPort = yaml_port or YamlAdapter()

    def validate(self, project_path: Path) -> ValidationResult:
        """Validate project_context.yaml.

        Args:
            project_path: Path to LAIDA project

        Returns:
            ValidationResult with outcome
        """
        context_file = project_path / "inputs" / "project_context.yaml"

        if not context_file.exists():
            return ValidationResult.failure(
                [f"project_context.yaml not found at {context_file}"]
            )

        try:
            data = self._yaml.parse_file(context_file)
        except ValueError as e:
            return ValidationResult.failure([f"YAML parse error: {e}"])

        return self._validate_content(data)

    def _validate_content(self, data: dict[str, Any]) -> ValidationResult:
        """Validate parsed context content.

        Args:
            data: Parsed YAML data

        Returns:
            ValidationResult with outcome
        """
        errors: list[str] = []
        warnings: list[str] = []

        # Get the context data
        context = data.get("project_context", data)

        # Check required fields
        for field in self.REQUIRED_FIELDS:
            if field not in context or not context[field]:
                errors.append(f"Required field '{field}' is missing or empty")

        # BR-011: mission-critical requires budget >= 2000
        criticality = context.get("criticality", "").lower()
        budget = context.get("budget", 0)

        if criticality == "mission-critical" and budget < 2000:
            errors.append(
                f"BR-011: mission-critical projects require budget >= 2000 "
                f"(current: {budget})"
            )

        # BR-012: business-critical requires budget >= 500
        if criticality == "business-critical" and budget < 500:
            errors.append(
                f"BR-012: business-critical projects require budget >= 500 "
                f"(current: {budget})"
            )

        # Warnings for missing optional fields
        for field in self.OPTIONAL_FIELDS:
            if field not in context:
                warnings.append(f"Optional field '{field}' not provided")

        if errors:
            return ValidationResult.failure(errors)
        if warnings:
            return ValidationResult.with_warnings(warnings)
        return ValidationResult.success()

    def check_required_fields(self, project_path: Path) -> list[str]:
        """Get list of missing required fields.

        Args:
            project_path: Path to LAIDA project

        Returns:
            List of missing field names
        """
        result = self.validate(project_path)
        missing = []
        for error in result.errors:
            if "Required field" in error:
                # Extract field name from error message
                start = error.find("'") + 1
                end = error.find("'", start)
                if start > 0 and end > start:
                    missing.append(error[start:end])
        return missing
