"""Pydantic models for migration operations.

This module defines the data structures used during container migration
between runtimes.
"""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any

from pydantic import BaseModel, ConfigDict, Field


class MigrationStatus(str, Enum):
    """Status of a migration operation."""

    PENDING = "pending"
    VALIDATING = "validating"
    EXPORTING = "exporting"
    IMPORTING = "importing"
    UPDATING_STORE = "updating_store"
    CLEANING_UP = "cleaning_up"
    COMPLETED = "completed"
    FAILED = "failed"
    ROLLED_BACK = "rolled_back"


class FeatureWarningSeverity(str, Enum):
    """Severity level for feature warnings."""

    INFO = "info"
    WARNING = "warning"
    CRITICAL = "critical"


class FeatureWarning(BaseModel):
    """Warning about feature incompatibility during migration.

    Features that cannot be perfectly translated between runtimes
    generate warnings to inform the user.
    """

    feature: str
    """Name of the feature (e.g., 'pause', 'cpu_shares')."""

    source_value: str | None = None
    """The value in the source configuration, if applicable."""

    target_behavior: str
    """Description of how the feature will be handled in the target runtime."""

    severity: FeatureWarningSeverity = FeatureWarningSeverity.WARNING
    """Severity level: info (FYI), warning (may affect behavior), critical (blocks migration)."""

    def __str__(self) -> str:
        """Format warning for display."""
        msg = f"{self.feature}: {self.target_behavior}"
        if self.source_value:
            msg = f"{msg} (source: {self.source_value})"
        return msg


class MigrationPlan(BaseModel):
    """Plan for a container migration.

    Created by analyzing the source container and target runtime
    before executing the migration. Can be used for dry-run preview.
    """

    container_name: str
    """Name of the container to migrate."""

    source_runtime: str
    """Runtime the container is currently on."""

    target_runtime: str
    """Runtime to migrate the container to."""

    preserve_source: bool = True
    """Whether to keep the source container after migration."""

    dry_run: bool = False
    """If True, only show what would be done without executing."""

    # Calculated fields
    estimated_size_bytes: int | None = None
    """Estimated size of the container filesystem in bytes."""

    feature_warnings: list[FeatureWarning] = Field(default_factory=list)
    """Warnings about feature incompatibilities."""

    translated_config: dict[str, Any] | None = None
    """The container config translated for the target runtime."""

    @property
    def has_critical_warnings(self) -> bool:
        """Check if any warnings are critical."""
        return any(
            w.severity == FeatureWarningSeverity.CRITICAL for w in self.feature_warnings
        )

    @property
    def estimated_size_mb(self) -> float | None:
        """Get estimated size in MB."""
        if self.estimated_size_bytes is None:
            return None
        return self.estimated_size_bytes / (1024 * 1024)


class MigrationState(BaseModel):
    """Current state of an ongoing or completed migration.

    Tracks progress through the migration phases and stores
    information needed for rollback on failure.
    """

    plan: MigrationPlan
    """The migration plan being executed."""

    status: MigrationStatus = MigrationStatus.PENDING
    """Current status of the migration."""

    started_at: datetime | None = None
    """When the migration started."""

    completed_at: datetime | None = None
    """When the migration completed (success or failure)."""

    # Progress tracking
    export_path: Path | None = None
    """Path to the exported filesystem tarball."""

    source_container_id: str | None = None
    """Container ID in the source runtime."""

    target_container_id: str | None = None
    """Container ID in the target runtime (after import)."""

    # Error information
    error: str | None = None
    """Error message if migration failed."""

    error_stage: MigrationStatus | None = None
    """The stage at which the error occurred."""

    # Rollback info
    can_rollback: bool = True
    """Whether rollback is possible."""

    rollback_actions: list[str] = Field(default_factory=list)
    """Actions performed during rollback or notes about manual cleanup."""

    # Source state preservation
    source_was_running: bool = False
    """Whether the source container was running before migration."""

    model_config = ConfigDict(arbitrary_types_allowed=True)

    @property
    def is_complete(self) -> bool:
        """Check if migration is in a terminal state."""
        return self.status in {
            MigrationStatus.COMPLETED,
            MigrationStatus.FAILED,
            MigrationStatus.ROLLED_BACK,
        }

    @property
    def is_success(self) -> bool:
        """Check if migration completed successfully."""
        return self.status == MigrationStatus.COMPLETED

    def mark_started(self) -> None:
        """Mark migration as started."""
        self.started_at = datetime.now()
        self.status = MigrationStatus.VALIDATING

    def mark_completed(self) -> None:
        """Mark migration as successfully completed."""
        self.completed_at = datetime.now()
        self.status = MigrationStatus.COMPLETED

    def mark_failed(self, error: str, stage: MigrationStatus | None = None) -> None:
        """Mark migration as failed.

        Args:
            error: Error message.
            stage: The stage at which failure occurred.
        """
        self.completed_at = datetime.now()
        self.status = MigrationStatus.FAILED
        self.error = error
        self.error_stage = stage or self.status

    def mark_rolled_back(self) -> None:
        """Mark migration as rolled back after failure."""
        self.status = MigrationStatus.ROLLED_BACK


class RuntimeCapabilities(BaseModel):
    """Capabilities and limitations of a container runtime.

    Used by the FeatureTranslator to determine what features
    can be migrated between runtimes.
    """

    name: str
    """Runtime name."""

    supports_pause: bool = False
    """Whether the runtime supports pause/unpause."""

    supports_snapshots: bool = True
    """Whether the runtime supports snapshots."""

    supports_copy: bool = True
    """Whether the runtime supports file copy operations."""

    supports_cpu_shares: bool = True
    """Whether CPU shares are supported (vs vCPU count)."""

    supports_cpu_count: bool = True
    """Whether CPU count limits are supported."""

    supports_memory_limit: bool = True
    """Whether memory limits are supported."""

    supports_privileged: bool = True
    """Whether privileged mode is supported."""

    supports_capabilities: bool = True
    """Whether Linux capabilities can be added/dropped."""

    supports_read_only: bool = True
    """Whether read-only root filesystem is supported."""

    memory_granularity_mb: int = 1
    """Memory allocation granularity in MB (libvirt rounds to 4MB)."""

    filesystem_type: str = "overlay"
    """Type of filesystem (overlay, directory, disk)."""

    requires_root_export: bool = False
    """Whether export requires root privileges."""

    requires_root_import: bool = False
    """Whether import requires root privileges."""
