"""
Environment models for releaseops.

Environments (dev, staging, prod) map bundle IDs to specific versions,
enabling environment-based resolution like support-agent@prod → 2.1.0.
"""

from __future__ import annotations

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


@dataclass(frozen=True)
class PromotionPolicy:
    """Policy governing what's required to promote into an environment."""

    requires_eval: bool = False
    requires_approval: bool = False
    approval_teams: List[str] = field(default_factory=list)
    soak_hours: int = 0  # Required soak time before promotion to next env

    def to_dict(self) -> dict:
        d: dict = {
            "requires_eval": self.requires_eval,
            "requires_approval": self.requires_approval,
        }
        if self.approval_teams:
            d["approval_teams"] = list(self.approval_teams)
        if self.soak_hours > 0:
            d["soak_hours"] = self.soak_hours
        return d

    @classmethod
    def from_dict(cls, data: dict) -> PromotionPolicy:
        return cls(
            requires_eval=data.get("requires_eval", False),
            requires_approval=data.get("requires_approval", False),
            approval_teams=data.get("approval_teams", []),
            soak_hours=data.get("soak_hours", 0),
        )


@dataclass(frozen=True)
class EnvironmentMetadata:
    """Metadata for an environment configuration."""

    last_updated: str = ""  # ISO 8601 timestamp
    updated_by: str = ""

    def to_dict(self) -> dict:
        return {
            "last_updated": self.last_updated,
            "updated_by": self.updated_by,
        }

    @classmethod
    def from_dict(cls, data: dict) -> EnvironmentMetadata:
        return cls(
            last_updated=data.get("last_updated", ""),
            updated_by=data.get("updated_by", ""),
        )


@dataclass(frozen=True)
class BundleMapping:
    """A single bundle-to-version mapping within an environment."""

    bundle_id: str
    version: str

    def __post_init__(self) -> None:
        if not self.bundle_id:
            raise ValueError("bundle_id cannot be empty")
        if not self.version:
            raise ValueError("version cannot be empty")


@dataclass(frozen=True)
class EnvironmentConfig:
    """
    Environment configuration — maps bundle IDs to versions.

    Stored as YAML in .releaseops/environments/{env-name}.yaml.
    Resolves queries like "What version of support-agent is in prod?"
    """

    environment: str  # "dev", "staging", "prod"
    bundle_mappings: Dict[str, str] = field(default_factory=dict)  # bundle_id -> version
    promotion_policy: Optional[PromotionPolicy] = None
    metadata: Optional[EnvironmentMetadata] = None

    def __post_init__(self) -> None:
        if not self.environment:
            raise ValueError("environment name cannot be empty")

    def get_version(self, bundle_id: str) -> Optional[str]:
        """Get the version of a bundle in this environment."""
        return self.bundle_mappings.get(bundle_id)

    def has_bundle(self, bundle_id: str) -> bool:
        """Check if a bundle is mapped in this environment."""
        return bundle_id in self.bundle_mappings

    @property
    def bundle_ids(self) -> List[str]:
        """All bundle IDs mapped in this environment."""
        return list(self.bundle_mappings.keys())

    def to_dict(self) -> dict:
        d: dict = {
            "environment": self.environment,
            "bundle_mappings": dict(self.bundle_mappings),
        }
        if self.promotion_policy:
            d["promotion_policy"] = self.promotion_policy.to_dict()
        if self.metadata:
            d["metadata"] = self.metadata.to_dict()
        return d

    @classmethod
    def from_dict(cls, data: dict) -> EnvironmentConfig:
        if "environment" not in data:
            raise ValueError("EnvironmentConfig missing required field: 'environment'")

        promotion_policy = None
        if "promotion_policy" in data:
            promotion_policy = PromotionPolicy.from_dict(data["promotion_policy"])

        metadata = None
        if "metadata" in data:
            metadata = EnvironmentMetadata.from_dict(data["metadata"])

        return cls(
            environment=data["environment"],
            bundle_mappings=data.get("bundle_mappings", {}),
            promotion_policy=promotion_policy,
            metadata=metadata,
        )
