"""
RunManifest for execution provenance tracking.

Implements FR-5: RunManifest Persistence
Complete record of execution with all inputs, outputs, and metadata.
"""

from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, Any, List, Optional


@dataclass
class ResolvedInput:
    """
    Record of a resolved input artifact.

    Attributes:
        name: Artifact name
        artifact_id: Artifact ID
        space_id: Space where artifact was found
        digest: Content digest
    """
    name: str
    artifact_id: str
    space_id: str
    digest: str

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "name": self.name,
            "artifact_id": self.artifact_id,
            "space_id": self.space_id,
            "digest": self.digest,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ResolvedInput":
        """Create from dictionary."""
        return cls(
            name=data["name"],
            artifact_id=data["artifact_id"],
            space_id=data["space_id"],
            digest=data["digest"],
        )


@dataclass
class DependencyEdge:
    """
    Dependency edge in execution graph.

    Attributes:
        source_id: Source artifact/run ID
        target_id: Target artifact/run ID
        edge_type: Type of dependency (requires, generates, includes)
    """
    source_id: str
    target_id: str
    edge_type: str

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "source_id": self.source_id,
            "target_id": self.target_id,
            "edge_type": self.edge_type,
        }


@dataclass
class OutputArtifact:
    """
    Artifact produced by execution.

    Attributes:
        artifact_id: Artifact ID
        name: Artifact name
        digest: Content digest
        artifact_type: Type of artifact
    """
    artifact_id: str
    name: str
    digest: str
    artifact_type: str

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return {
            "artifact_id": self.artifact_id,
            "name": self.name,
            "digest": self.digest,
            "artifact_type": self.artifact_type,
        }


@dataclass
class RunManifest:
    """
    Complete execution manifest with provenance.

    Implements FR-5: RunManifest Persistence

    The RunManifest provides complete traceability for a prompt execution,
    capturing all inputs, outputs, configuration, and metadata.

    Implements FR-5.2: RunManifest contents:
    - Template metadata
    - Resolved inputs and their digests
    - CompiledPrompt digest
    - Model configuration
    - Output artifacts and digests
    - Dependency edges
    - Validation results
    - Impact debt records (if applicable)

    Attributes:
        run_id: ID of associated run
        template_metadata: Template information
        resolved_inputs: List of resolved input artifacts
        compiled_prompt_digest: Digest of compiled prompt
        model_config: Model configuration used
        output_artifacts: List of produced artifacts
        dependency_edges: Dependency graph edges
        validation_results: Quality validation results
        impact_debt: Suppressed recomputation records
        timing_metadata: Execution timing information
        created_at: Manifest creation time
    """
    run_id: str
    template_metadata: Dict[str, Any]
    resolved_inputs: List[ResolvedInput] = field(default_factory=list)
    compiled_prompt_digest: str = ""
    model_config: Dict[str, Any] = field(default_factory=dict)
    output_artifacts: List[OutputArtifact] = field(default_factory=list)
    dependency_edges: List[DependencyEdge] = field(default_factory=list)
    validation_results: Dict[str, Any] = field(default_factory=dict)
    impact_debt: List[Dict[str, Any]] = field(default_factory=list)
    timing_metadata: Dict[str, float] = field(default_factory=dict)
    created_at: datetime = field(default_factory=datetime.utcnow)

    @classmethod
    def create(
        cls,
        run_id: str,
        template_id: str,
        template_name: str,
        template_digest: str,
    ) -> "RunManifest":
        """
        Create a new manifest.

        Args:
            run_id: Run ID
            template_id: Template ID
            template_name: Template name
            template_digest: Template content digest

        Returns:
            New RunManifest instance
        """
        return cls(
            run_id=run_id,
            template_metadata={
                "template_id": template_id,
                "template_name": template_name,
                "template_digest": template_digest,
            },
        )

    def add_resolved_input(
        self,
        name: str,
        artifact_id: str,
        space_id: str,
        digest: str,
    ) -> None:
        """
        Add a resolved input artifact.

        Args:
            name: Artifact name
            artifact_id: Artifact ID
            space_id: Space ID
            digest: Content digest
        """
        self.resolved_inputs.append(
            ResolvedInput(
                name=name,
                artifact_id=artifact_id,
                space_id=space_id,
                digest=digest,
            )
        )

    def add_output_artifact(
        self,
        artifact_id: str,
        name: str,
        digest: str,
        artifact_type: str,
    ) -> None:
        """
        Add an output artifact.

        Args:
            artifact_id: Artifact ID
            name: Artifact name
            digest: Content digest
            artifact_type: Artifact type
        """
        self.output_artifacts.append(
            OutputArtifact(
                artifact_id=artifact_id,
                name=name,
                digest=digest,
                artifact_type=artifact_type,
            )
        )

    def add_dependency_edge(
        self,
        source_id: str,
        target_id: str,
        edge_type: str,
    ) -> None:
        """
        Add a dependency edge.

        Args:
            source_id: Source ID
            target_id: Target ID
            edge_type: Edge type
        """
        self.dependency_edges.append(
            DependencyEdge(
                source_id=source_id,
                target_id=target_id,
                edge_type=edge_type,
            )
        )

    def set_timing(self, stage: str, duration_seconds: float) -> None:
        """
        Record timing for a stage.

        Args:
            stage: Stage name
            duration_seconds: Duration in seconds
        """
        self.timing_metadata[stage] = duration_seconds

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "run_id": self.run_id,
            "template_metadata": self.template_metadata,
            "resolved_inputs": [inp.to_dict() for inp in self.resolved_inputs],
            "compiled_prompt_digest": self.compiled_prompt_digest,
            "model_config": self.model_config,
            "output_artifacts": [out.to_dict() for out in self.output_artifacts],
            "dependency_edges": [edge.to_dict() for edge in self.dependency_edges],
            "validation_results": self.validation_results,
            "impact_debt": self.impact_debt,
            "timing_metadata": self.timing_metadata,
            "created_at": self.created_at.isoformat(),
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "RunManifest":
        """Create from dictionary."""
        return cls(
            run_id=data["run_id"],
            template_metadata=data["template_metadata"],
            resolved_inputs=[
                ResolvedInput.from_dict(inp) for inp in data.get("resolved_inputs", [])
            ],
            compiled_prompt_digest=data.get("compiled_prompt_digest", ""),
            model_config=data.get("model_config", {}),
            output_artifacts=[
                OutputArtifact(**out) for out in data.get("output_artifacts", [])
            ],
            dependency_edges=[
                DependencyEdge(**edge) for edge in data.get("dependency_edges", [])
            ],
            validation_results=data.get("validation_results", {}),
            impact_debt=data.get("impact_debt", []),
            timing_metadata=data.get("timing_metadata", {}),
            created_at=datetime.fromisoformat(data["created_at"]),
        )
