"""Pydantic models for container update management."""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Annotated

from pydantic import BaseModel, ConfigDict, Field, field_validator


class UpdateScheduleType(str, Enum):
    """Schedule type for automatic updates."""

    MANUAL = "manual"
    DAILY = "daily"
    WEEKLY = "weekly"
    MONTHLY = "monthly"


class UpdateStatus(str, Enum):
    """Status of an update operation."""

    PENDING = "pending"
    CHECKING = "checking"
    DOWNLOADING = "downloading"
    APPLYING = "applying"
    VERIFYING = "verifying"
    ROLLING_BACK = "rolling_back"
    COMPLETED = "completed"
    FAILED = "failed"
    SKIPPED = "skipped"


class RollbackStrategy(str, Enum):
    """Strategy for handling update failures."""

    AUTO = "auto"
    MANUAL = "manual"
    NONE = "none"


class UpdateSchedule(BaseModel):
    """Schedule configuration for automatic updates."""

    model_config = ConfigDict(frozen=True)

    schedule_type: UpdateScheduleType = UpdateScheduleType.MANUAL
    time_of_day: Annotated[str, Field(pattern=r"^\d{2}:\d{2}$")] = Field(
        default="03:00", description="Time of day for updates (HH:MM)"
    )
    day_of_week: Annotated[int, Field(ge=0, le=6)] = Field(
        default=0, description="Day of week for weekly updates (0=Monday)"
    )
    day_of_month: Annotated[int, Field(ge=1, le=28)] = Field(
        default=1, description="Day of month for monthly updates"
    )
    enabled: bool = True

    @field_validator("time_of_day")
    @classmethod
    def validate_time(cls, v: str) -> str:
        """Validate time format."""
        parts = v.split(":")
        hour, minute = int(parts[0]), int(parts[1])
        if not (0 <= hour <= 23 and 0 <= minute <= 59):
            raise ValueError("Invalid time format")
        return v


class UpdatePolicy(BaseModel):
    """Update policy for a container."""

    model_config = ConfigDict(frozen=True)

    container_name: Annotated[
        str, Field(min_length=1, max_length=64)
    ] = Field(..., description="Container name")

    schedule: UpdateSchedule = Field(default_factory=UpdateSchedule)

    update_packages: bool = Field(
        default=True, description="Update system packages"
    )
    update_base_image: bool = Field(
        default=False, description="Rebuild from latest base image (destructive)"
    )

    rollback_strategy: RollbackStrategy = Field(
        default=RollbackStrategy.AUTO,
        description="How to handle update failures",
    )
    pre_update_snapshot: bool = Field(
        default=True, description="Create snapshot before update"
    )
    snapshot_retention: Annotated[int, Field(ge=1, le=100)] = Field(
        default=3, description="Number of update snapshots to retain"
    )

    health_check_after: bool = Field(
        default=True, description="Run health check after update"
    )
    health_check_timeout: Annotated[int, Field(ge=10, le=600)] = Field(
        default=120, description="Timeout for post-update health check"
    )

    auto_restart: bool = Field(
        default=True, description="Restart container after update"
    )

    packages_to_skip: list[str] = Field(
        default_factory=list, description="Packages to exclude from updates"
    )


class PackageUpdate(BaseModel):
    """Information about a single package update."""

    model_config = ConfigDict(frozen=True)

    package_name: str
    old_version: str | None = None
    new_version: str | None = None
    action: str = "upgrade"


class UpdateResult(BaseModel):
    """Result of an update operation."""

    model_config = ConfigDict(frozen=False)

    container_name: str
    started_at: datetime = Field(default_factory=datetime.now)
    completed_at: datetime | None = None
    status: UpdateStatus = UpdateStatus.PENDING

    snapshot_name: str | None = Field(
        default=None, description="Name of pre-update snapshot"
    )

    packages_checked: int = 0
    packages_upgraded: int = 0
    packages_updated: list[PackageUpdate] = Field(default_factory=list)

    health_check_passed: bool | None = None
    health_check_output: str | None = None

    rolled_back: bool = False
    rollback_reason: str | None = None

    error_message: str | None = None
    error_output: str | None = None

    @property
    def success(self) -> bool:
        """Return True if update completed successfully."""
        return self.status == UpdateStatus.COMPLETED

    @property
    def duration_seconds(self) -> float | None:
        """Calculate duration of update operation."""
        if self.completed_at is None:
            return None
        return (self.completed_at - self.started_at).total_seconds()


class UpdateCheck(BaseModel):
    """Result of checking for available updates."""

    model_config = ConfigDict(frozen=True)

    container_name: str
    checked_at: datetime = Field(default_factory=datetime.now)
    updates_available: bool = False
    package_count: int = 0
    packages: list[PackageUpdate] = Field(default_factory=list)
    error_message: str | None = None


class UpdateHistory(BaseModel):
    """History of update operations for a container."""

    model_config = ConfigDict(frozen=False)

    container_name: str
    results: list[UpdateResult] = Field(default_factory=list)
    last_update: datetime | None = None
    last_check: datetime | None = None
    total_updates: int = 0
    successful_updates: int = 0
    failed_updates: int = 0
    rollbacks: int = 0

    def add_result(self, result: UpdateResult) -> None:
        """Add an update result to history."""
        self.results.append(result)
        self.last_update = result.completed_at or result.started_at
        self.total_updates += 1

        if result.status == UpdateStatus.COMPLETED:
            self.successful_updates += 1
        elif result.status == UpdateStatus.FAILED:
            self.failed_updates += 1

        if result.rolled_back:
            self.rollbacks += 1

        # Keep only last 100 results
        if len(self.results) > 100:
            self.results = self.results[-100:]


class SchedulerState(BaseModel):
    """State of the update scheduler."""

    model_config = ConfigDict(frozen=False)

    running: bool = False
    started_at: datetime | None = None
    last_check: datetime | None = None
    next_scheduled: dict[str, datetime] = Field(
        default_factory=dict, description="Next scheduled update per container"
    )
