"""Abstract base class for Copy-on-Write backends."""

from __future__ import annotations

from abc import ABC, abstractmethod
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from gaol.cow.models import (
        DatasetConfig,
        DatasetState,
        PoolConfig,
        PoolState,
        Snapshot,
    )


class CoWBackendBase(ABC):
    """Abstract interface for CoW filesystem backends."""

    @property
    @abstractmethod
    def name(self) -> str:
        """Return the backend name."""
        ...

    @abstractmethod
    def is_available(self) -> bool:
        """Check if this backend is available on the system."""
        ...

    @abstractmethod
    def validate_mount_point(self, path: Path) -> bool:
        """Check if path is a valid mount point for this backend.

        Args:
            path: Path to check

        Returns:
            True if path is a valid CoW filesystem mount
        """
        ...

    # Pool operations

    @abstractmethod
    def init_pool(self, config: PoolConfig) -> PoolState:
        """Initialize a pool on an existing CoW filesystem.

        Creates a gaol subvolume/dataset under the mount point.

        Args:
            config: Pool configuration

        Returns:
            Initial pool state

        Raises:
            InvalidMountPointError: If mount point is not valid
            CoWError: If initialization fails
        """
        ...

    @abstractmethod
    def get_pool_info(self, state: PoolState) -> PoolState:
        """Get updated pool information (usage, status).

        Args:
            state: Current pool state

        Returns:
            Updated pool state with current usage info
        """
        ...

    @abstractmethod
    def destroy_pool(self, state: PoolState) -> None:
        """Destroy a pool and all its contents.

        Args:
            state: Pool state

        Raises:
            CoWError: If destruction fails
        """
        ...

    # Dataset operations

    @abstractmethod
    def create_dataset(self, pool: PoolState, config: DatasetConfig) -> DatasetState:
        """Create a new dataset (subvolume).

        Args:
            pool: Parent pool state
            config: Dataset configuration

        Returns:
            New dataset state

        Raises:
            DatasetExistsError: If dataset already exists
            CoWError: If creation fails
        """
        ...

    @abstractmethod
    def get_dataset_info(self, pool: PoolState, state: DatasetState) -> DatasetState:
        """Get updated dataset information.

        Args:
            pool: Parent pool state
            state: Current dataset state

        Returns:
            Updated dataset state
        """
        ...

    @abstractmethod
    def destroy_dataset(self, pool: PoolState, state: DatasetState) -> None:
        """Destroy a dataset.

        Args:
            pool: Parent pool state
            state: Dataset state

        Raises:
            CoWError: If destruction fails
        """
        ...

    @abstractmethod
    def set_quota(self, pool: PoolState, state: DatasetState, quota_mb: int | None) -> DatasetState:
        """Set or remove quota on a dataset.

        Args:
            pool: Parent pool state
            state: Dataset state
            quota_mb: Quota in MB, or None to remove

        Returns:
            Updated dataset state
        """
        ...

    # Snapshot operations

    @abstractmethod
    def create_snapshot(
        self, pool: PoolState, dataset: DatasetState, name: str
    ) -> Snapshot:
        """Create a snapshot of a dataset.

        Args:
            pool: Parent pool state
            dataset: Dataset to snapshot
            name: Snapshot name

        Returns:
            New snapshot

        Raises:
            SnapshotExistsError: If snapshot already exists
            CoWError: If creation fails
        """
        ...

    @abstractmethod
    def list_snapshots(
        self, pool: PoolState, dataset: DatasetState
    ) -> list[Snapshot]:
        """List all snapshots of a dataset.

        Args:
            pool: Parent pool state
            dataset: Dataset to list snapshots for

        Returns:
            List of snapshots
        """
        ...

    @abstractmethod
    def delete_snapshot(
        self, pool: PoolState, dataset: DatasetState, snapshot: Snapshot
    ) -> None:
        """Delete a snapshot.

        Args:
            pool: Parent pool state
            dataset: Parent dataset
            snapshot: Snapshot to delete

        Raises:
            SnapshotHasClonesError: If snapshot has dependent clones
            CoWError: If deletion fails
        """
        ...

    @abstractmethod
    def rollback(
        self, pool: PoolState, dataset: DatasetState, snapshot: Snapshot
    ) -> DatasetState:
        """Rollback a dataset to a snapshot.

        Args:
            pool: Parent pool state
            dataset: Dataset to rollback
            snapshot: Snapshot to rollback to

        Returns:
            Updated dataset state

        Raises:
            CoWError: If rollback fails
        """
        ...

    # Clone operations

    @abstractmethod
    def create_clone(
        self, pool: PoolState, snapshot: Snapshot, name: str
    ) -> DatasetState:
        """Create a writable clone from a snapshot.

        Args:
            pool: Pool to create clone in
            snapshot: Source snapshot
            name: Clone name

        Returns:
            New dataset state for the clone

        Raises:
            DatasetExistsError: If name already exists
            CoWError: If cloning fails
        """
        ...

    @abstractmethod
    def promote_clone(
        self, pool: PoolState, clone: DatasetState
    ) -> DatasetState:
        """Promote a clone to an independent dataset.

        After promotion, the clone no longer depends on its source snapshot.

        Args:
            pool: Parent pool state
            clone: Clone dataset to promote

        Returns:
            Updated dataset state

        Raises:
            CoWError: If promotion fails
        """
        ...

    # Send/Receive operations

    @abstractmethod
    def send(
        self,
        pool: PoolState,
        dataset: DatasetState,
        snapshot: Snapshot,
        dest: Path,
        base_snapshot: Snapshot | None = None,
    ) -> Path:
        """Send a snapshot to a file.

        Args:
            pool: Parent pool state
            dataset: Dataset containing snapshot
            snapshot: Snapshot to send
            dest: Destination file path
            base_snapshot: Base snapshot for incremental send

        Returns:
            Path to the created stream file

        Raises:
            SendReceiveError: If send fails
        """
        ...

    @abstractmethod
    def receive(
        self,
        pool: PoolState,
        source: Path,
        name: str | None = None,
    ) -> DatasetState:
        """Receive a snapshot stream into a new dataset.

        Args:
            pool: Pool to receive into
            source: Path to stream file
            name: Name for the received dataset (auto-detected if None)

        Returns:
            New dataset state

        Raises:
            SendReceiveError: If receive fails
        """
        ...
