"""
Data models for quality validation and halting policies.

Implements FR-9: QualityGate Validation
Implements FR-10: Halting and Refinement Policy
"""

import uuid
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Dict, List, Optional


class GateType(Enum):
    """Type classification for quality gates."""
    SCHEMA = "schema"
    PATTERN = "pattern"
    CUSTOM = "custom"


class ValidationStatus(Enum):
    """Outcome status of a quality gate check."""
    PASS = "pass"
    FAIL = "fail"
    WARNING = "warning"
    SKIPPED = "skipped"


class HaltDecision(Enum):
    """Decision outcome from halting policy evaluation."""
    CONTINUE = "continue"
    HALT_QUALITY_MET = "halted_quality_met"
    HALT_ITERATION_LIMIT = "halted_iteration_limit"
    HALT_BUDGET_EXHAUSTED = "halted_budget_exhausted"
    HALT_NO_IMPROVEMENT = "halted_no_improvement"


@dataclass
class ValidationDiagnostic:
    """
    Single diagnostic message from a quality gate.

    Attributes:
        code: Machine-readable diagnostic code
        message: Human-readable description
        severity: Severity level (error, warning, info)
    """
    code: str
    message: str
    severity: str = "error"

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "code": self.code,
            "message": self.message,
            "severity": self.severity,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ValidationDiagnostic":
        """Create from dictionary."""
        return cls(
            code=data["code"],
            message=data["message"],
            severity=data.get("severity", "error"),
        )


@dataclass
class ValidationResult:
    """
    Result of applying a quality gate to an artifact.

    Implements FR-9.3: Record pass/fail results and diagnostics.

    Attributes:
        id: Unique result identifier
        gate_id: ID of the quality gate that produced this result
        gate_type: Type of the quality gate
        artifact_id: ID of the validated artifact
        status: Pass/fail outcome
        score: Optional quality score (0.0-1.0)
        diagnostics: List of diagnostic messages
        validated_at: When validation occurred
    """
    id: str
    gate_id: str
    gate_type: GateType
    artifact_id: str
    status: ValidationStatus
    score: Optional[float] = None
    diagnostics: List[ValidationDiagnostic] = field(default_factory=list)
    validated_at: datetime = field(default_factory=datetime.utcnow)

    @classmethod
    def create(
        cls,
        gate_id: str,
        gate_type: GateType,
        artifact_id: str,
        status: ValidationStatus,
        score: Optional[float] = None,
        diagnostics: Optional[List[ValidationDiagnostic]] = None,
    ) -> "ValidationResult":
        """Create a new ValidationResult."""
        return cls(
            id=str(uuid.uuid4()),
            gate_id=gate_id,
            gate_type=gate_type,
            artifact_id=artifact_id,
            status=status,
            score=score,
            diagnostics=diagnostics or [],
        )

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "id": self.id,
            "gate_id": self.gate_id,
            "gate_type": self.gate_type.value,
            "artifact_id": self.artifact_id,
            "status": self.status.value,
            "score": self.score,
            "diagnostics": [d.to_dict() for d in self.diagnostics],
            "validated_at": self.validated_at.isoformat(),
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ValidationResult":
        """Create from dictionary."""
        return cls(
            id=data["id"],
            gate_id=data["gate_id"],
            gate_type=GateType(data["gate_type"]),
            artifact_id=data["artifact_id"],
            status=ValidationStatus(data["status"]),
            score=data.get("score"),
            diagnostics=[
                ValidationDiagnostic.from_dict(d)
                for d in data.get("diagnostics", [])
            ],
            validated_at=datetime.fromisoformat(data["validated_at"]),
        )


class QualityGate(ABC):
    """
    Abstract base class for quality gates.

    Implements FR-9.1/FR-9.2: Pluggable validation framework
    supporting multiple gates per artifact.
    """

    def __init__(self, gate_id: str, name: str, gate_type: GateType):
        self.id = gate_id
        self.name = name
        self.gate_type = gate_type

    @abstractmethod
    def validate(self, content: str, artifact_id: str) -> ValidationResult:
        """
        Validate content against this quality gate.

        Args:
            content: Content to validate
            artifact_id: ID of the artifact being validated

        Returns:
            ValidationResult with status and diagnostics
        """
        pass


@dataclass
class QualityPolicy:
    """
    Configuration for halting and refinement policy.

    Implements FR-10.1: Configurable QualityPolicies.

    Attributes:
        max_iterations: Maximum refinement iterations
        min_improvement: Minimum score improvement to continue
        fail_on_gate_failure: Whether any gate failure halts execution
        resource_budget: Maximum total runs allowed
        required_gate_ids: Gate IDs that must pass for quality to be met
    """
    max_iterations: int = 3
    min_improvement: float = 0.05
    fail_on_gate_failure: bool = True
    resource_budget: int = 10
    required_gate_ids: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "max_iterations": self.max_iterations,
            "min_improvement": self.min_improvement,
            "fail_on_gate_failure": self.fail_on_gate_failure,
            "resource_budget": self.resource_budget,
            "required_gate_ids": self.required_gate_ids,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "QualityPolicy":
        """Create from dictionary."""
        return cls(
            max_iterations=data.get("max_iterations", 3),
            min_improvement=data.get("min_improvement", 0.05),
            fail_on_gate_failure=data.get("fail_on_gate_failure", True),
            resource_budget=data.get("resource_budget", 10),
            required_gate_ids=data.get("required_gate_ids", []),
        )


@dataclass
class HaltingRecord:
    """
    Record of a halting decision.

    Implements FR-10.3: Record halting decisions in the RunManifest.

    Attributes:
        decision: The halting decision
        iteration: Current iteration number
        max_iterations: Maximum allowed iterations
        scores: Score history across iterations
        reason: Human-readable reason for decision
        recorded_at: When the decision was made
    """
    decision: HaltDecision
    iteration: int
    max_iterations: int
    scores: List[float] = field(default_factory=list)
    reason: str = ""
    recorded_at: datetime = field(default_factory=datetime.utcnow)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "decision": self.decision.value,
            "iteration": self.iteration,
            "max_iterations": self.max_iterations,
            "scores": self.scores,
            "reason": self.reason,
            "recorded_at": self.recorded_at.isoformat(),
        }


@dataclass
class RefinementResult:
    """
    Result of a refinement loop execution.

    Attributes:
        iterations_run: Number of iterations executed
        final_results: Validation results from the last iteration
        halting_record: Record of the halting decision
        all_results: Validation results from all iterations
        run_ids: List of run IDs produced during refinement
    """
    iterations_run: int
    final_results: List[ValidationResult] = field(default_factory=list)
    halting_record: Optional[HaltingRecord] = None
    all_results: List[List[ValidationResult]] = field(default_factory=list)
    run_ids: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "iterations_run": self.iterations_run,
            "final_results": [r.to_dict() for r in self.final_results],
            "halting_record": self.halting_record.to_dict() if self.halting_record else None,
            "all_results": [
                [r.to_dict() for r in iteration]
                for iteration in self.all_results
            ],
            "run_ids": self.run_ids,
        }
