"""
Artifact models for releaseops.

Artifacts are the atomic units that compose a bundle — prompts, policies,
and other versioned content that define AI behavior.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
from typing import Optional


class ArtifactType(str, Enum):
    """Types of artifacts that can be included in a bundle."""

    PROMPT = "prompt"
    POLICY_CONTEXT = "policy_context"
    POLICY_TOOLS = "policy_tools"
    POLICY_SAFETY = "policy_safety"


@dataclass(frozen=True)
class ArtifactRef:
    """
    Reference to a versioned artifact.

    For prompts: ref = "prompts/system@1.0.1" (managed by promptops)
    For policies: path = "policies/context-support.yaml" (file path)
    """

    artifact_type: ArtifactType
    role: str  # e.g., "system", "planner", "context", "tools", "safety"
    ref: Optional[str] = None  # promptops ref like "prompts/system@1.0.1"
    path: Optional[str] = None  # file path like "policies/context-support.yaml"
    content_hash: Optional[str] = None  # "sha256:abc123..."

    def __post_init__(self) -> None:
        if not self.ref and not self.path:
            raise ValueError("ArtifactRef must have either 'ref' or 'path'")
        if self.ref and self.path:
            raise ValueError("ArtifactRef cannot have both 'ref' and 'path'")

    @property
    def is_prompt(self) -> bool:
        return self.artifact_type == ArtifactType.PROMPT

    @property
    def is_policy(self) -> bool:
        return self.artifact_type in (
            ArtifactType.POLICY_CONTEXT,
            ArtifactType.POLICY_TOOLS,
            ArtifactType.POLICY_SAFETY,
        )

    @property
    def identifier(self) -> str:
        """Human-readable identifier for this artifact."""
        return self.ref or self.path or self.role

    def to_dict(self) -> dict:
        """Serialize to dictionary for YAML output."""
        d: dict = {}
        if self.ref:
            d["ref"] = self.ref
        if self.path:
            d["path"] = self.path
        if self.content_hash:
            d["content_hash"] = self.content_hash
        return d

    @classmethod
    def from_dict(cls, role: str, artifact_type: ArtifactType, data: dict) -> ArtifactRef:
        """Deserialize from dictionary."""
        return cls(
            artifact_type=artifact_type,
            role=role,
            ref=data.get("ref"),
            path=data.get("path"),
            content_hash=data.get("content_hash"),
        )


@dataclass(frozen=True)
class Artifact:
    """
    A resolved artifact with its actual content loaded.

    Used at runtime after resolving an ArtifactRef to real content.
    """

    ref: ArtifactRef
    content: str  # The actual template/policy content
    content_hash: str  # Verified SHA-256 hash

    def __post_init__(self) -> None:
        if not self.content_hash.startswith("sha256:"):
            raise ValueError(
                f"content_hash must start with 'sha256:', got '{self.content_hash}'"
            )
