"""Volume manager for encrypted storage lifecycle."""

from __future__ import annotations

from pathlib import Path

from gaol.config import get_data_dir
from gaol.secrets import (
    SecretNotFoundError,
    decode_key,
    encode_key,
    generate_master_key,
    get_secret_store,
)
from gaol.storage.backends import EncryptionBackendBase, get_backend
from gaol.storage.exceptions import (
    VolumeExistsError,
    VolumeKeyError,
    VolumeNotFoundError,
)
from gaol.storage.models import (
    EncryptionBackend,
    VolumeConfig,
    VolumeState,
    VolumeStatus,
)
from gaol.storage.store import VolumeStore


class VolumeManager:
    """Manage encrypted volume lifecycle.

    Handles creation, unlocking, locking, and destruction of encrypted
    volumes. Integrates with the secrets module for key storage.

    Example:
        >>> manager = VolumeManager()
        >>> config = VolumeConfig(name="mydata", size_mb=1024)
        >>> state = manager.create(config)
        >>> state = manager.unlock("mydata")
        >>> print(state.mount_point)  # Use this path
        >>> manager.lock("mydata")
    """

    def __init__(self, store: VolumeStore | None = None) -> None:
        """Initialize the volume manager."""
        self._store = store or VolumeStore()
        self._backends: dict[EncryptionBackend, EncryptionBackendBase] = {}

    def _get_backend(self, backend_type: EncryptionBackend) -> EncryptionBackendBase:
        """Get or create a backend instance."""
        if backend_type not in self._backends:
            self._backends[backend_type] = get_backend(backend_type)
        return self._backends[backend_type]

    def _get_storage_dir(self) -> Path:
        """Get the storage directory for volumes."""
        storage_dir = get_data_dir() / "volumes" / "data"
        storage_dir.mkdir(parents=True, exist_ok=True)
        return storage_dir

    def _generate_and_store_key(self, secret_name: str) -> bytes:
        """Generate a new key and store it in secrets."""
        secret_store = get_secret_store()

        # Generate 256-bit key
        key = generate_master_key()

        # Store encoded in secrets
        secret_store.create(
            name=secret_name,
            value=encode_key(key),
            description="Encrypted volume key (auto-generated)",
        )

        return key

    def _get_key(self, secret_name: str) -> bytes:
        """Retrieve key from secrets."""
        secret_store = get_secret_store()

        try:
            encoded = secret_store.get(secret_name)
            return decode_key(encoded)
        except SecretNotFoundError as e:
            raise VolumeKeyError(secret_name, "Key not found in secrets store") from e

    def _delete_key(self, secret_name: str) -> None:
        """Delete key from secrets."""
        secret_store = get_secret_store()
        try:
            secret_store.delete(secret_name)
        except SecretNotFoundError:
            pass  # Key already deleted

    def create(self, config: VolumeConfig) -> VolumeState:
        """Create a new encrypted volume.

        Args:
            config: Volume configuration

        Returns:
            Initial volume state

        Raises:
            VolumeExistsError: If volume already exists
            BackendNotAvailableError: If backend not available
        """
        # Check if exists
        if self._store.exists(config.name):
            raise VolumeExistsError(config.name)

        # Get backend
        backend = self._get_backend(config.backend)

        # Generate and store key
        secret_name = f"volume_{config.name}_key"
        key = self._generate_and_store_key(secret_name)

        try:
            # Create volume
            state = backend.create(config, key, self._get_storage_dir())

            # Persist state
            self._store.save(state)

            return state
        except Exception:
            # Cleanup key on failure
            self._delete_key(secret_name)
            raise

    def unlock(self, name: str) -> VolumeState:
        """Unlock (decrypt) a volume.

        Args:
            name: Volume name

        Returns:
            Updated volume state with mount_point

        Raises:
            VolumeNotFoundError: If volume doesn't exist
            VolumeKeyError: If key is incorrect
        """
        state = self._store.get(name)
        if not state:
            raise VolumeNotFoundError(name)

        backend = self._get_backend(state.backend)

        # Get key from secrets
        key = self._get_key(state.key_secret_name)

        # Unlock
        new_state = backend.unlock(state, key)

        # Persist updated state
        self._store.save(new_state)

        return new_state

    def lock(self, name: str) -> VolumeState:
        """Lock (re-encrypt) a volume.

        Args:
            name: Volume name

        Returns:
            Updated volume state

        Raises:
            VolumeNotFoundError: If volume doesn't exist
            VolumeMountedError: If volume is mounted
        """
        state = self._store.get(name)
        if not state:
            raise VolumeNotFoundError(name)

        backend = self._get_backend(state.backend)
        new_state = backend.lock(state)

        self._store.save(new_state)
        return new_state

    def destroy(self, name: str, force: bool = False) -> None:
        """Destroy a volume and its key.

        Args:
            name: Volume name
            force: Force destruction even if unlocked

        Raises:
            VolumeNotFoundError: If volume doesn't exist
            VolumeUnlockedError: If volume is unlocked (unless force)
        """
        state = self._store.get(name)
        if not state:
            raise VolumeNotFoundError(name)

        # Lock first if force
        if force and state.status != VolumeStatus.LOCKED:
            try:
                state = self.lock(name)
            except Exception:
                pass  # Best effort

        backend = self._get_backend(state.backend)
        backend.destroy(state)

        # Delete key
        self._delete_key(state.key_secret_name)

        # Remove from store
        self._store.delete(name)

    def list(self) -> list[VolumeState]:
        """List all volumes.

        Returns:
            List of volume states
        """
        return self._store.list()

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

        Args:
            name: Volume name

        Returns:
            Volume state or None if not found
        """
        return self._store.get(name)

    def get_mount_path(self, name: str) -> Path | None:
        """Get the mount path for an unlocked volume.

        Args:
            name: Volume name

        Returns:
            Mount path or None if locked

        Raises:
            VolumeNotFoundError: If volume doesn't exist
        """
        state = self._store.get(name)
        if not state:
            raise VolumeNotFoundError(name)

        backend = self._get_backend(state.backend)
        return backend.get_mount_path(state)


# Global instance
_manager: VolumeManager | None = None


def get_volume_manager() -> VolumeManager:
    """Get the global volume manager instance."""
    global _manager
    if _manager is None:
        _manager = VolumeManager()
    return _manager
