"""Pydantic models for container build operations."""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Literal

from pydantic import BaseModel, Field


class BuildMethod(str, Enum):
    """Method for building an image from container."""

    COMMIT = "commit"  # Fast: docker commit
    DOCKERFILE = "dockerfile"  # Reproducible: generate and build Dockerfile


class PackageManager(str, Enum):
    """Package manager type for install commands."""

    APT = "apt"
    APK = "apk"
    DNF = "dnf"
    PACMAN = "pacman"
    UNKNOWN = "unknown"


class BuildConfig(BaseModel):
    """Configuration for building from container.

    Controls how the image is built from the container state.
    """

    container_name: str = Field(..., description="Source container name")
    base_image: str | None = Field(
        default=None, description="Base image (auto-detect if None)"
    )
    tag: str = Field(default="latest", description="Image tag")
    repository: str | None = Field(
        default=None, description="Repository name (default: container name)"
    )
    include_packages: bool = Field(
        default=True, description="Include installed packages in Dockerfile"
    )
    include_environment: bool = Field(
        default=True, description="Include environment variables"
    )
    labels: dict[str, str] = Field(
        default_factory=dict, description="Additional labels for image"
    )
    method: BuildMethod = Field(
        default=BuildMethod.COMMIT, description="Build method"
    )


class DockerfileInstruction(BaseModel):
    """Single Dockerfile instruction.

    Represents one line/directive in the generated Dockerfile.
    """

    instruction: str = Field(..., description="Instruction type (FROM, RUN, ENV, etc.)")
    arguments: str = Field(..., description="Instruction arguments")
    comment: str | None = Field(default=None, description="Optional comment")

    def render(self) -> str:
        """Render instruction as Dockerfile line."""
        lines = []
        if self.comment:
            lines.append(f"# {self.comment}")
        lines.append(f"{self.instruction} {self.arguments}")
        return "\n".join(lines)


class GeneratedDockerfile(BaseModel):
    """Generated Dockerfile with metadata.

    Contains all information needed to build a reproducible image.
    """

    container_name: str = Field(..., description="Source container name")
    base_image: str = Field(..., description="Base image used")
    instructions: list[DockerfileInstruction] = Field(
        default_factory=list, description="Dockerfile instructions"
    )
    packages: list[str] = Field(
        default_factory=list, description="Installed packages"
    )
    package_manager: PackageManager = Field(
        default=PackageManager.UNKNOWN, description="Package manager type"
    )
    environment: dict[str, str] = Field(
        default_factory=dict, description="Environment variables"
    )
    exposed_ports: list[int] = Field(
        default_factory=list, description="Exposed ports"
    )
    working_dir: str | None = Field(default=None, description="Working directory")
    entrypoint: list[str] | None = Field(default=None, description="Entrypoint")
    cmd: list[str] | None = Field(default=None, description="Default command")
    created_at: datetime = Field(
        default_factory=datetime.now, description="Generation timestamp"
    )

    def render(self) -> str:
        """Render complete Dockerfile content."""
        lines = [
            f"# Auto-generated by gaol from container: {self.container_name}",
            f"# Generated at: {self.created_at.isoformat()}",
            "",
        ]

        for instruction in self.instructions:
            lines.append(instruction.render())
            lines.append("")

        return "\n".join(lines)


class ImageLayer(BaseModel):
    """Information about a single image layer."""

    id: str = Field(..., description="Layer ID (truncated)")
    created_at: datetime | None = Field(default=None, description="Creation time")
    created_by: str = Field(default="", description="Command that created layer")
    size_bytes: int = Field(default=0, description="Layer size in bytes")
    comment: str | None = Field(default=None, description="Layer comment")


class ImageInfo(BaseModel):
    """Information about a built or existing image."""

    id: str = Field(..., description="Image ID")
    tags: list[str] = Field(default_factory=list, description="Image tags")
    size_bytes: int = Field(default=0, description="Total size in bytes")
    created_at: datetime | None = Field(default=None, description="Creation time")
    layers: list[ImageLayer] = Field(
        default_factory=list, description="Image layers"
    )
    architecture: str | None = Field(default=None, description="Target architecture")
    os: str = Field(default="linux", description="Target OS")
    labels: dict[str, str] = Field(default_factory=dict, description="Image labels")

    @property
    def layer_count(self) -> int:
        """Number of layers in image."""
        return len(self.layers)

    @property
    def primary_tag(self) -> str | None:
        """First tag if available."""
        return self.tags[0] if self.tags else None


class BuildResult(BaseModel):
    """Result of a build operation.

    Contains outcome, timing, and any error information.
    """

    success: bool = Field(..., description="Whether build succeeded")
    image_id: str | None = Field(default=None, description="Built image ID")
    tags: list[str] = Field(default_factory=list, description="Applied tags")
    dockerfile_path: str | None = Field(
        default=None, description="Path to generated Dockerfile"
    )
    build_log: list[str] = Field(
        default_factory=list, description="Build output log"
    )
    error: str | None = Field(default=None, description="Error message if failed")
    started_at: datetime = Field(
        default_factory=datetime.now, description="Build start time"
    )
    finished_at: datetime | None = Field(default=None, description="Build end time")

    @property
    def duration_seconds(self) -> float:
        """Build duration in seconds."""
        if self.finished_at is None:
            return 0.0
        return (self.finished_at - self.started_at).total_seconds()


class PushResult(BaseModel):
    """Result of a registry push operation."""

    success: bool = Field(..., description="Whether push succeeded")
    image_tag: str = Field(..., description="Pushed image tag")
    registry: str = Field(..., description="Target registry")
    digest: str | None = Field(default=None, description="Image digest")
    size_bytes: int = Field(default=0, description="Pushed size")
    error: str | None = Field(default=None, description="Error message if failed")
    started_at: datetime = Field(default_factory=datetime.now)
    finished_at: datetime | None = Field(default=None)

    @property
    def duration_seconds(self) -> float:
        """Push duration in seconds."""
        if self.finished_at is None:
            return 0.0
        return (self.finished_at - self.started_at).total_seconds()


class PushProgress(BaseModel):
    """Progress update during push operation."""

    status: str = Field(..., description="Status message")
    progress: str | None = Field(default=None, description="Progress bar")
    layer_id: str | None = Field(default=None, description="Layer being pushed")
    current: int = Field(default=0, description="Bytes pushed")
    total: int = Field(default=0, description="Total bytes")

    @property
    def percent(self) -> float:
        """Push progress percentage."""
        if self.total == 0:
            return 0.0
        return (self.current / self.total) * 100
