"""Pydantic models for encrypted storage."""

from __future__ import annotations

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

from pydantic import BaseModel, Field


class EncryptionBackend(str, Enum):
    """Encryption backend types."""

    LUKS = "luks"  # LUKS2 via cryptsetup
    GOCRYPTFS = "gocryptfs"  # FUSE-based encryption


class VolumeStatus(str, Enum):
    """Volume status states."""

    CREATED = "created"  # Volume created but not unlocked
    LOCKED = "locked"  # Volume exists but encrypted (locked)
    UNLOCKED = "unlocked"  # Volume decrypted and accessible
    MOUNTED = "mounted"  # Volume mounted to a container
    ERROR = "error"  # Volume in error state


class VolumeConfig(BaseModel):
    """Configuration for creating an encrypted volume."""

    name: Annotated[
        str,
        Field(min_length=1, max_length=64, pattern=r"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"),
    ]
    """Unique volume name."""

    backend: EncryptionBackend = EncryptionBackend.LUKS
    """Encryption backend to use."""

    size_mb: Annotated[int, Field(ge=10, description="Volume size in MB")] = 1024
    """Volume size in megabytes (only for LUKS)."""

    filesystem: str = "ext4"
    """Filesystem type for LUKS volumes."""

    cipher: str = "aes-xts-plain64"
    """LUKS cipher specification."""

    key_size: int = 256
    """Encryption key size in bits."""

    description: str | None = None
    """Optional volume description."""


class VolumeState(BaseModel):
    """Runtime state of an encrypted volume."""

    name: str
    """Unique volume name."""

    backend: EncryptionBackend
    """Encryption backend type."""

    status: VolumeStatus = VolumeStatus.CREATED
    """Current volume status."""

    # Paths
    storage_path: Path
    """Path to encrypted volume storage (image file or gocryptfs dir)."""

    mount_point: Path | None = None
    """Current mount point when unlocked/mounted."""

    # LUKS-specific
    loop_device: str | None = None
    """Loop device path (e.g., /dev/loop0) for LUKS volumes."""

    dm_name: str | None = None
    """Device mapper name for LUKS volumes."""

    # Metadata
    size_mb: int
    """Volume size in MB."""

    filesystem: str | None = None
    """Filesystem type."""

    created_at: datetime = Field(default_factory=datetime.now)
    """When the volume was created."""

    unlocked_at: datetime | None = None
    """When the volume was last unlocked."""

    # Key reference
    key_secret_name: str
    """Name of secret storing the encryption key."""

    description: str | None = None
    """Optional volume description."""


class VolumeMount(BaseModel):
    """Volume mount configuration for a container."""

    volume_name: str
    """Name of the encrypted volume."""

    container_path: str
    """Mount path inside the container."""

    read_only: bool = False
    """Mount as read-only."""

    auto_unlock: bool = True
    """Automatically unlock when mounting."""


class VolumesStore(BaseModel):
    """Persistence model for volume state."""

    version: int = 1
    """Store format version."""

    volumes: dict[str, VolumeState] = Field(default_factory=dict)
    """Map of volume name to state."""

    def add_volume(self, volume: VolumeState) -> None:
        """Add or update a volume."""
        self.volumes[volume.name] = volume

    def get_volume(self, name: str) -> VolumeState | None:
        """Get a volume by name."""
        return self.volumes.get(name)

    def remove_volume(self, name: str) -> bool:
        """Remove a volume, returns True if it existed."""
        if name in self.volumes:
            del self.volumes[name]
            return True
        return False

    def list_volumes(self) -> list[VolumeState]:
        """Get all volumes."""
        return list(self.volumes.values())
