"""Pydantic models for container backup 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 BackupScheduleType(str, Enum):
    """Schedule type for automatic backups."""

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


class BackupStatus(str, Enum):
    """Status of a backup operation."""

    PENDING = "pending"
    PREPARING = "preparing"
    SNAPSHOTTING = "snapshotting"
    UPLOADING = "uploading"
    VERIFYING = "verifying"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class BackupType(str, Enum):
    """Type of backup to create."""

    FULL = "full"  # Full filesystem export
    INCREMENTAL = "incremental"  # CoW send (delta from previous)
    CONFIG_ONLY = "config_only"  # Container config YAML only


class StorageBackendType(str, Enum):
    """Supported storage backends."""

    LOCAL = "local"
    S3 = "s3"


class BackupSchedule(BaseModel):
    """Schedule configuration for automatic backups."""

    model_config = ConfigDict(frozen=True)

    schedule_type: BackupScheduleType = BackupScheduleType.MANUAL
    time_of_day: Annotated[str, Field(pattern=r"^\d{2}:\d{2}$")] = Field(
        default="02:00", description="Time of day for backups (HH:MM)"
    )
    day_of_week: Annotated[int, Field(ge=0, le=6)] = Field(
        default=0, description="Day of week for weekly backups (0=Monday)"
    )
    day_of_month: Annotated[int, Field(ge=1, le=28)] = Field(
        default=1, description="Day of month for monthly backups"
    )
    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 RetentionPolicy(BaseModel):
    """Retention policy for backups."""

    model_config = ConfigDict(frozen=True)

    keep_last: Annotated[int | None, Field(ge=1)] = Field(
        default=7, description="Keep last N backups"
    )
    keep_daily: Annotated[int | None, Field(ge=0)] = Field(
        default=7, description="Keep N daily backups (one per day)"
    )
    keep_weekly: Annotated[int | None, Field(ge=0)] = Field(
        default=4, description="Keep N weekly backups (one per week)"
    )
    keep_monthly: Annotated[int | None, Field(ge=0)] = Field(
        default=6, description="Keep N monthly backups (one per month)"
    )
    max_age_days: Annotated[int | None, Field(ge=1)] = Field(
        default=90, description="Delete backups older than N days"
    )
    max_total_size_mb: Annotated[int | None, Field(ge=1)] = Field(
        default=None, description="Max total backup size in MB"
    )


class StorageConfig(BaseModel):
    """Configuration for backup storage location."""

    model_config = ConfigDict(frozen=True)

    backend: StorageBackendType = StorageBackendType.LOCAL
    path: str | None = Field(default=None, description="Local path or bucket path")

    # S3-specific options
    bucket: str | None = Field(default=None, description="S3 bucket name")
    region: str | None = Field(default=None, description="AWS region")
    endpoint_url: str | None = Field(
        default=None, description="S3-compatible endpoint (MinIO, etc.)"
    )
    access_key_id: str | None = Field(
        default=None, description="AWS access key (if not using instance credentials)"
    )
    secret_access_key: str | None = Field(
        default=None, description="AWS secret key"
    )
    prefix: str | None = Field(default=None, description="Key prefix within bucket")


class BackupPolicy(BaseModel):
    """Backup 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: BackupSchedule = Field(default_factory=BackupSchedule)
    retention: RetentionPolicy = Field(default_factory=RetentionPolicy)
    storage: StorageConfig | None = Field(
        default=None, description="Override global storage config"
    )

    backup_type: BackupType = Field(
        default=BackupType.FULL, description="Default backup type"
    )
    prefer_incremental: bool = Field(
        default=True,
        description="Use incremental if CoW available, fallback to full",
    )
    compression: str = Field(
        default="gzip", description="Compression: gzip, xz, or none"
    )

    pre_backup_commands: list[str] = Field(
        default_factory=list, description="Commands to run before backup"
    )
    post_backup_commands: list[str] = Field(
        default_factory=list, description="Commands to run after backup"
    )

    verify_after_backup: bool = Field(
        default=True, description="Verify backup integrity after creation"
    )
    notify_on_failure: bool = Field(
        default=True, description="Send notification on backup failure"
    )


class BackupMetadata(BaseModel):
    """Metadata for a completed backup."""

    model_config = ConfigDict(frozen=True)

    backup_id: str = Field(..., description="Unique identifier (UUID)")
    container_name: str
    backup_name: str = Field(..., description="Human-readable name")
    backup_type: BackupType

    created_at: datetime
    completed_at: datetime | None = None

    size_bytes: int = 0
    compressed_size_bytes: int | None = None
    compression: str | None = None

    # For incremental backups
    is_incremental: bool = False
    base_backup_id: str | None = Field(
        default=None, description="Previous backup this is based on"
    )
    cow_snapshot: str | None = Field(
        default=None, description="CoW snapshot name if used"
    )

    # Storage location
    storage_backend: StorageBackendType = StorageBackendType.LOCAL
    storage_path: str = Field(..., description="Full path/key to backup file")

    # Verification
    checksum: str | None = Field(default=None, description="SHA256 hash")
    verified: bool = False

    # Container state at backup time
    container_was_running: bool = False
    runtime: str | None = None


class BackupResult(BaseModel):
    """Result of a backup operation."""

    model_config = ConfigDict(frozen=False)

    container_name: str
    backup_name: str
    backup_id: str
    started_at: datetime = Field(default_factory=datetime.now)
    completed_at: datetime | None = None
    status: BackupStatus = BackupStatus.PENDING
    backup_type: BackupType = BackupType.FULL

    size_bytes: int = 0
    storage_path: str | None = None
    checksum: str | None = None

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

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

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


class RestoreResult(BaseModel):
    """Result of a restore operation."""

    model_config = ConfigDict(frozen=False)

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

    pre_restore_snapshot: str | None = Field(
        default=None, description="Safety snapshot created before restore"
    )

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

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

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


class BackupHistory(BaseModel):
    """History of backup operations for a container."""

    model_config = ConfigDict(frozen=False)

    container_name: str
    backups: list[BackupMetadata] = Field(default_factory=list)
    last_backup: datetime | None = None
    last_successful_backup: datetime | None = None
    total_backups: int = 0
    successful_backups: int = 0
    failed_backups: int = 0
    total_size_bytes: int = 0

    def add_backup(self, metadata: BackupMetadata) -> None:
        """Add a backup to history."""
        self.backups.append(metadata)
        self.last_backup = metadata.created_at
        self.total_backups += 1

        if metadata.completed_at:
            self.successful_backups += 1
            self.last_successful_backup = metadata.completed_at
            self.total_size_bytes += metadata.size_bytes
        else:
            self.failed_backups += 1

        # Keep only last 500 entries in memory
        if len(self.backups) > 500:
            self.backups = self.backups[-500:]

    def remove_backup(self, backup_id: str) -> bool:
        """Remove a backup from history by ID."""
        for i, backup in enumerate(self.backups):
            if backup.backup_id == backup_id:
                removed = self.backups.pop(i)
                self.total_size_bytes -= removed.size_bytes
                return True
        return False


class GlobalBackupConfig(BaseModel):
    """Global backup configuration."""

    model_config = ConfigDict(frozen=False)

    default_storage: StorageConfig = Field(default_factory=StorageConfig)
    default_retention: RetentionPolicy = Field(default_factory=RetentionPolicy)
    scheduler_enabled: bool = True
    parallel_backups: Annotated[int, Field(ge=1, le=10)] = 1


class SchedulerState(BaseModel):
    """State of the backup 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 backup per container"
    )
    currently_running: list[str] = Field(
        default_factory=list, description="Containers currently being backed up"
    )
