"""Tests for the Copy-on-Write filesystem module."""

from __future__ import annotations

import tempfile
from datetime import datetime
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from gaol.cow.models import (
    CloneConfig,
    CoWBackendType,
    DatasetConfig,
    DatasetState,
    DatasetsStore,
    PoolConfig,
    PoolsStore,
    PoolState,
    PoolStatus,
    SendOptions,
    Snapshot,
)
from gaol.cow.exceptions import (
    BackendNotAvailableError,
    CloneNotFoundError,
    CoWError,
    DatasetExistsError,
    DatasetNotEmptyError,
    DatasetNotFoundError,
    InsufficientPermissionsError,
    InvalidMountPointError,
    PoolExistsError,
    PoolNotEmptyError,
    PoolNotFoundError,
    QuotaError,
    SendReceiveError,
    SnapshotExistsError,
    SnapshotHasClonesError,
    SnapshotNotFoundError,
)


# =============================================================================
# Model Tests
# =============================================================================


class TestPoolConfig:
    """Tests for PoolConfig model."""

    def test_default_values(self) -> None:
        """Test default configuration values."""
        config = PoolConfig(name="test", mount_point=Path("/mnt/btrfs"))
        assert config.backend == CoWBackendType.BTRFS
        assert config.compression is None
        assert config.quota_mb is None
        assert config.description is None

    def test_btrfs_config(self) -> None:
        """Test btrfs backend configuration."""
        config = PoolConfig(
            name="mypool",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt/btrfs"),
            compression="zstd",
        )
        assert config.backend == CoWBackendType.BTRFS
        assert config.compression == "zstd"

    def test_zfs_config(self) -> None:
        """Test ZFS backend configuration."""
        config = PoolConfig(
            name="tank",
            backend=CoWBackendType.ZFS,
            mount_point=Path("/tank"),
            compression="lz4",
            description="ZFS tank pool",
        )
        assert config.backend == CoWBackendType.ZFS
        assert config.compression == "lz4"
        assert config.description == "ZFS tank pool"

    def test_name_validation(self) -> None:
        """Test pool name validation."""
        # Valid names
        PoolConfig(name="test", mount_point=Path("/mnt"))
        PoolConfig(name="test-pool", mount_point=Path("/mnt"))
        PoolConfig(name="test_pool", mount_point=Path("/mnt"))
        PoolConfig(name="Test123", mount_point=Path("/mnt"))

        # Invalid names
        with pytest.raises(ValueError):
            PoolConfig(name="", mount_point=Path("/mnt"))  # Empty
        with pytest.raises(ValueError):
            PoolConfig(name="test pool", mount_point=Path("/mnt"))  # Space
        with pytest.raises(ValueError):
            PoolConfig(name="test/pool", mount_point=Path("/mnt"))  # Slash

    def test_quota_validation(self) -> None:
        """Test pool quota validation."""
        # Valid quota
        PoolConfig(name="test", mount_point=Path("/mnt"), quota_mb=1024)

        # Invalid quota
        with pytest.raises(ValueError):
            PoolConfig(name="test", mount_point=Path("/mnt"), quota_mb=0)


class TestPoolState:
    """Tests for PoolState model."""

    def test_initial_state(self) -> None:
        """Test initial pool state."""
        state = PoolState(
            name="test",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt/btrfs"),
            gaol_path=Path("/mnt/btrfs/gaol"),
        )
        assert state.status == PoolStatus.ONLINE
        assert state.total_bytes == 0
        assert state.used_bytes == 0
        assert state.free_bytes == 0

    def test_usage_percent(self) -> None:
        """Test usage percentage calculation."""
        state = PoolState(
            name="test",
            backend=CoWBackendType.ZFS,
            mount_point=Path("/tank"),
            gaol_path=Path("/tank/gaol"),
            total_bytes=1000000000,  # 1 GB
            used_bytes=250000000,    # 250 MB
            free_bytes=750000000,    # 750 MB
        )
        assert state.usage_percent == pytest.approx(25.0)

    def test_usage_percent_empty(self) -> None:
        """Test usage percentage with empty pool."""
        state = PoolState(
            name="test",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt"),
            gaol_path=Path("/mnt/gaol"),
            total_bytes=0,
        )
        assert state.usage_percent == 0.0


class TestDatasetConfig:
    """Tests for DatasetConfig model."""

    def test_default_values(self) -> None:
        """Test default dataset configuration."""
        config = DatasetConfig(name="data", pool="mypool")
        assert config.quota_mb is None
        assert config.compression is None
        assert config.description is None

    def test_full_config(self) -> None:
        """Test full dataset configuration."""
        config = DatasetConfig(
            name="containers",
            pool="mypool",
            quota_mb=10240,
            compression="lz4",
            description="Container storage",
        )
        assert config.name == "containers"
        assert config.pool == "mypool"
        assert config.quota_mb == 10240
        assert config.compression == "lz4"


class TestDatasetState:
    """Tests for DatasetState model."""

    def test_initial_state(self) -> None:
        """Test initial dataset state."""
        state = DatasetState(
            name="data",
            pool="mypool",
            path=Path("/mnt/btrfs/gaol/data"),
        )
        assert state.quota_mb is None
        assert state.used_bytes == 0
        assert state.snapshots == []

    def test_full_name(self) -> None:
        """Test full dataset name property."""
        state = DatasetState(
            name="containers",
            pool="mypool",
            path=Path("/mnt/btrfs/gaol/containers"),
        )
        assert state.full_name == "mypool/containers"

    def test_with_snapshots(self) -> None:
        """Test dataset state with snapshots."""
        state = DatasetState(
            name="data",
            pool="mypool",
            path=Path("/mnt/btrfs/gaol/data"),
            snapshots=["v1", "v2", "v3"],
        )
        assert len(state.snapshots) == 3
        assert "v1" in state.snapshots


class TestSnapshot:
    """Tests for Snapshot model."""

    def test_basic_snapshot(self) -> None:
        """Test basic snapshot creation."""
        snap = Snapshot(
            name="v1",
            dataset="data",
            pool="mypool",
        )
        assert snap.readonly is True
        assert snap.used_bytes == 0

    def test_full_name(self) -> None:
        """Test full snapshot name property."""
        snap = Snapshot(
            name="backup-20260113",
            dataset="containers",
            pool="mypool",
        )
        assert snap.full_name == "mypool/containers@backup-20260113"

    def test_name_validation(self) -> None:
        """Test snapshot name validation."""
        # Valid names
        Snapshot(name="v1", dataset="data", pool="mypool")
        Snapshot(name="backup-20260113", dataset="data", pool="mypool")

        # Invalid names
        with pytest.raises(ValueError):
            Snapshot(name="v1@invalid", dataset="data", pool="mypool")
        with pytest.raises(ValueError):
            Snapshot(name="data/v1", dataset="data", pool="mypool")


class TestCloneConfig:
    """Tests for CloneConfig model."""

    def test_basic_config(self) -> None:
        """Test basic clone configuration."""
        config = CloneConfig(
            name="data-test",
            source_snapshot="mypool/data@v1",
            pool="mypool",
        )
        assert config.name == "data-test"
        assert config.source_snapshot == "mypool/data@v1"

    def test_source_validation(self) -> None:
        """Test source snapshot validation."""
        # Valid source
        CloneConfig(name="test", source_snapshot="pool/dataset@snap", pool="pool")

        # Invalid source (missing @)
        with pytest.raises(ValueError):
            CloneConfig(name="test", source_snapshot="pool/dataset", pool="pool")


class TestSendOptions:
    """Tests for SendOptions model."""

    def test_default_options(self) -> None:
        """Test default send options."""
        opts = SendOptions()
        assert opts.base_snapshot is None
        assert opts.compressed is True
        assert opts.raw is False

    def test_incremental_send(self) -> None:
        """Test incremental send options."""
        opts = SendOptions(base_snapshot="v1", compressed=True)
        assert opts.base_snapshot == "v1"


# =============================================================================
# Store Tests
# =============================================================================


class TestPoolsStore:
    """Tests for PoolsStore model."""

    def test_empty_store(self) -> None:
        """Test empty pools store."""
        store = PoolsStore()
        assert store.version == 1
        assert len(store.pools) == 0
        assert store.list_pools() == []

    def test_add_pool(self) -> None:
        """Test adding a pool to store."""
        store = PoolsStore()
        pool = PoolState(
            name="test",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt"),
            gaol_path=Path("/mnt/gaol"),
        )
        store.add_pool(pool)
        assert "test" in store.pools
        assert store.get_pool("test") == pool

    def test_remove_pool(self) -> None:
        """Test removing a pool from store."""
        store = PoolsStore()
        pool = PoolState(
            name="test",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt"),
            gaol_path=Path("/mnt/gaol"),
        )
        store.add_pool(pool)
        assert store.remove_pool("test") is True
        assert store.get_pool("test") is None
        assert store.remove_pool("nonexistent") is False


class TestDatasetsStore:
    """Tests for DatasetsStore model."""

    def test_empty_store(self) -> None:
        """Test empty datasets store."""
        store = DatasetsStore()
        assert store.version == 1
        assert len(store.datasets) == 0

    def test_add_dataset(self) -> None:
        """Test adding a dataset to store."""
        store = DatasetsStore()
        dataset = DatasetState(
            name="data",
            pool="mypool",
            path=Path("/mnt/gaol/data"),
        )
        store.add_dataset(dataset)
        assert store.get_dataset("mypool", "data") == dataset

    def test_list_datasets_by_pool(self) -> None:
        """Test listing datasets filtered by pool."""
        store = DatasetsStore()
        ds1 = DatasetState(name="data", pool="pool1", path=Path("/a"))
        ds2 = DatasetState(name="data", pool="pool2", path=Path("/b"))
        ds3 = DatasetState(name="other", pool="pool1", path=Path("/c"))
        store.add_dataset(ds1)
        store.add_dataset(ds2)
        store.add_dataset(ds3)

        pool1_datasets = store.list_datasets("pool1")
        assert len(pool1_datasets) == 2

        all_datasets = store.list_datasets(None)
        assert len(all_datasets) == 3


# =============================================================================
# Exception Tests
# =============================================================================


class TestExceptions:
    """Tests for CoW exception classes."""

    def test_pool_not_found_error(self) -> None:
        """Test PoolNotFoundError."""
        error = PoolNotFoundError("mypool")
        assert error.pool_name == "mypool"
        assert "mypool" in str(error)

    def test_pool_exists_error(self) -> None:
        """Test PoolExistsError."""
        error = PoolExistsError("mypool")
        assert error.pool_name == "mypool"
        assert "already exists" in str(error)

    def test_pool_not_empty_error(self) -> None:
        """Test PoolNotEmptyError."""
        error = PoolNotEmptyError("mypool", 5)
        assert error.pool_name == "mypool"
        assert error.dataset_count == 5
        assert "5 datasets" in str(error)

    def test_dataset_not_found_error(self) -> None:
        """Test DatasetNotFoundError."""
        error = DatasetNotFoundError("mypool", "data")
        assert error.pool == "mypool"
        assert error.dataset_name == "data"
        assert "mypool/data" in str(error)

    def test_dataset_exists_error(self) -> None:
        """Test DatasetExistsError."""
        error = DatasetExistsError("mypool", "data")
        assert "already exists" in str(error)

    def test_dataset_not_empty_error(self) -> None:
        """Test DatasetNotEmptyError."""
        error = DatasetNotEmptyError("mypool", "data", 3)
        assert error.snapshot_count == 3
        assert "3 snapshots" in str(error)

    def test_snapshot_not_found_error(self) -> None:
        """Test SnapshotNotFoundError."""
        error = SnapshotNotFoundError("mypool", "data", "v1")
        assert error.snapshot_name == "v1"
        assert "mypool/data@v1" in str(error)

    def test_snapshot_exists_error(self) -> None:
        """Test SnapshotExistsError."""
        error = SnapshotExistsError("mypool", "data", "v1")
        assert "already exists" in str(error)

    def test_snapshot_has_clones_error(self) -> None:
        """Test SnapshotHasClonesError."""
        error = SnapshotHasClonesError("mypool", "data", "v1")
        assert "clones" in str(error)

    def test_backend_not_available_error(self) -> None:
        """Test BackendNotAvailableError."""
        error = BackendNotAvailableError("btrfs", "btrfs-progs not installed")
        assert error.backend == "btrfs"
        assert error.reason == "btrfs-progs not installed"
        assert "not available" in str(error)

    def test_invalid_mount_point_error(self) -> None:
        """Test InvalidMountPointError."""
        error = InvalidMountPointError("/mnt/test", "btrfs")
        assert error.path == "/mnt/test"
        assert error.expected_type == "btrfs"

    def test_send_receive_error(self) -> None:
        """Test SendReceiveError."""
        error = SendReceiveError("send", "connection refused")
        assert error.operation == "send"
        assert error.reason == "connection refused"
        assert "send" in str(error)


# =============================================================================
# Store Persistence Tests
# =============================================================================


class TestCoWStore:
    """Tests for CoWStore persistence."""

    def test_save_and_load_pool(self) -> None:
        """Test saving and loading pool state."""
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            pool = PoolState(
                name="test",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt/btrfs"),
                gaol_path=Path("/mnt/btrfs/gaol"),
            )
            store.save_pool(pool)

            loaded = store.get_pool("test")
            assert loaded is not None
            assert loaded.name == "test"
            assert loaded.backend == CoWBackendType.BTRFS

    def test_save_and_load_dataset(self) -> None:
        """Test saving and loading dataset state."""
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            dataset = DatasetState(
                name="data",
                pool="mypool",
                path=Path("/mnt/gaol/data"),
                snapshots=["v1", "v2"],
            )
            store.save_dataset(dataset)

            loaded = store.get_dataset("mypool", "data")
            assert loaded is not None
            assert loaded.name == "data"
            assert loaded.pool == "mypool"
            assert len(loaded.snapshots) == 2

    def test_delete_pool(self) -> None:
        """Test deleting a pool."""
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            pool = PoolState(
                name="test",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt"),
                gaol_path=Path("/mnt/gaol"),
            )
            store.save_pool(pool)
            assert store.pool_exists("test")

            store.delete_pool("test")
            assert not store.pool_exists("test")

    def test_list_pools(self) -> None:
        """Test listing pools."""
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            for name in ["pool1", "pool2", "pool3"]:
                pool = PoolState(
                    name=name,
                    backend=CoWBackendType.BTRFS,
                    mount_point=Path(f"/mnt/{name}"),
                    gaol_path=Path(f"/mnt/{name}/gaol"),
                )
                store.save_pool(pool)

            pools = store.list_pools()
            assert len(pools) == 3
            names = [p.name for p in pools]
            assert "pool1" in names
            assert "pool2" in names
            assert "pool3" in names


# =============================================================================
# Backend Tests
# =============================================================================


class TestBtrfsBackend:
    """Tests for btrfs backend."""

    def test_name(self) -> None:
        """Test backend name."""
        from gaol.cow.backends.btrfs import BtrfsBackend

        backend = BtrfsBackend()
        assert backend.name == "btrfs"

    @patch("shutil.which")
    def test_is_available_true(self, mock_which: MagicMock) -> None:
        """Test availability check when btrfs is installed."""
        from gaol.cow.backends.btrfs import BtrfsBackend

        mock_which.return_value = "/usr/bin/btrfs"
        backend = BtrfsBackend()
        assert backend.is_available() is True

    @patch("shutil.which")
    def test_is_available_false(self, mock_which: MagicMock) -> None:
        """Test availability check when btrfs is not installed."""
        from gaol.cow.backends.btrfs import BtrfsBackend

        mock_which.return_value = None
        backend = BtrfsBackend()
        assert backend.is_available() is False


class TestZfsBackend:
    """Tests for ZFS backend."""

    def test_name(self) -> None:
        """Test backend name."""
        from gaol.cow.backends.zfs import ZfsBackend

        backend = ZfsBackend()
        assert backend.name == "zfs"

    @patch("shutil.which")
    def test_is_available_true(self, mock_which: MagicMock) -> None:
        """Test availability check when ZFS is installed."""
        from gaol.cow.backends.zfs import ZfsBackend

        mock_which.return_value = "/usr/sbin/zfs"
        backend = ZfsBackend()
        assert backend.is_available() is True

    @patch("shutil.which")
    def test_is_available_false(self, mock_which: MagicMock) -> None:
        """Test availability check when ZFS is not installed."""
        from gaol.cow.backends.zfs import ZfsBackend

        def which_side_effect(cmd: str) -> str | None:
            return None

        mock_which.side_effect = which_side_effect
        backend = ZfsBackend()
        assert backend.is_available() is False

    def test_parse_size(self) -> None:
        """Test ZFS size string parsing."""
        from gaol.cow.backends.zfs import ZfsBackend

        backend = ZfsBackend()
        assert backend._parse_size("1K") == 1024
        assert backend._parse_size("1M") == 1024 * 1024
        assert backend._parse_size("1G") == 1024 * 1024 * 1024
        assert backend._parse_size("1T") == 1024 * 1024 * 1024 * 1024
        assert backend._parse_size("1024") == 1024
        assert backend._parse_size("-") == 0
        assert backend._parse_size("") == 0


# =============================================================================
# Backend Factory Tests
# =============================================================================


class TestBackendFactory:
    """Tests for backend factory."""

    @patch("gaol.cow.backends.btrfs.BtrfsBackend.is_available")
    def test_get_btrfs_backend(self, mock_available: MagicMock) -> None:
        """Test getting btrfs backend."""
        from gaol.cow.backends import get_backend

        mock_available.return_value = True
        backend = get_backend(CoWBackendType.BTRFS)
        assert backend.name == "btrfs"

    @patch("gaol.cow.backends.zfs.ZfsBackend.is_available")
    def test_get_zfs_backend(self, mock_available: MagicMock) -> None:
        """Test getting ZFS backend."""
        from gaol.cow.backends import get_backend

        mock_available.return_value = True
        backend = get_backend(CoWBackendType.ZFS)
        assert backend.name == "zfs"

    @patch("gaol.cow.backends.btrfs.BtrfsBackend.is_available")
    def test_backend_not_available(self, mock_available: MagicMock) -> None:
        """Test error when backend not available."""
        from gaol.cow.backends import get_backend

        mock_available.return_value = False
        with pytest.raises(BackendNotAvailableError):
            get_backend(CoWBackendType.BTRFS)


# =============================================================================
# Manager Tests
# =============================================================================


class TestCoWManager:
    """Tests for CoWManager."""

    def test_init_manager(self) -> None:
        """Test manager initialization."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))
            manager = CoWManager(store=store)
            assert manager._store is store

    @patch("gaol.cow.backends.btrfs.BtrfsBackend.is_available")
    @patch("gaol.cow.backends.btrfs.BtrfsBackend.init_pool")
    def test_init_pool(
        self,
        mock_init_pool: MagicMock,
        mock_available: MagicMock,
    ) -> None:
        """Test pool initialization."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        mock_available.return_value = True
        mock_init_pool.return_value = PoolState(
            name="test",
            backend=CoWBackendType.BTRFS,
            mount_point=Path("/mnt/btrfs"),
            gaol_path=Path("/mnt/btrfs/gaol"),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))
            manager = CoWManager(store=store)

            config = PoolConfig(name="test", mount_point=Path("/mnt/btrfs"))
            pool = manager.init_pool(config)

            assert pool.name == "test"
            assert store.pool_exists("test")

    def test_init_pool_already_exists(self) -> None:
        """Test error when pool already exists."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            # Pre-populate store
            pool = PoolState(
                name="test",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt"),
                gaol_path=Path("/mnt/gaol"),
            )
            store.save_pool(pool)

            manager = CoWManager(store=store)
            config = PoolConfig(name="test", mount_point=Path("/mnt"))

            with pytest.raises(PoolExistsError):
                manager.init_pool(config)

    def test_list_pools(self) -> None:
        """Test listing pools."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            for name in ["pool1", "pool2"]:
                pool = PoolState(
                    name=name,
                    backend=CoWBackendType.BTRFS,
                    mount_point=Path(f"/mnt/{name}"),
                    gaol_path=Path(f"/mnt/{name}/gaol"),
                )
                store.save_pool(pool)

            manager = CoWManager(store=store)
            pools = manager.list_pools()
            assert len(pools) == 2

    @patch("gaol.cow.backends.btrfs.BtrfsBackend.is_available")
    @patch("gaol.cow.backends.btrfs.BtrfsBackend.destroy_pool")
    def test_destroy_pool(
        self,
        mock_destroy: MagicMock,
        mock_available: MagicMock,
    ) -> None:
        """Test pool destruction."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        mock_available.return_value = True

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            pool = PoolState(
                name="test",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt"),
                gaol_path=Path("/mnt/gaol"),
            )
            store.save_pool(pool)

            manager = CoWManager(store=store)
            manager.destroy_pool("test", force=True)

            assert not store.pool_exists("test")
            mock_destroy.assert_called_once()

    def test_destroy_pool_not_found(self) -> None:
        """Test error when destroying nonexistent pool."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))
            manager = CoWManager(store=store)

            with pytest.raises(PoolNotFoundError):
                manager.destroy_pool("nonexistent")

    @patch("gaol.cow.backends.btrfs.BtrfsBackend.is_available")
    @patch("gaol.cow.backends.btrfs.BtrfsBackend.create_dataset")
    def test_create_dataset(
        self,
        mock_create: MagicMock,
        mock_available: MagicMock,
    ) -> None:
        """Test dataset creation."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        mock_available.return_value = True
        mock_create.return_value = DatasetState(
            name="data",
            pool="test",
            path=Path("/mnt/gaol/data"),
        )

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            pool = PoolState(
                name="test",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt"),
                gaol_path=Path("/mnt/gaol"),
            )
            store.save_pool(pool)

            manager = CoWManager(store=store)
            config = DatasetConfig(name="data", pool="test")
            dataset = manager.create_dataset(config)

            assert dataset.name == "data"
            assert store.dataset_exists("test", "data")

    def test_create_dataset_pool_not_found(self) -> None:
        """Test error when creating dataset in nonexistent pool."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))
            manager = CoWManager(store=store)

            config = DatasetConfig(name="data", pool="nonexistent")
            with pytest.raises(PoolNotFoundError):
                manager.create_dataset(config)

    def test_list_datasets(self) -> None:
        """Test listing datasets."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))

            for name in ["data", "containers"]:
                ds = DatasetState(
                    name=name,
                    pool="test",
                    path=Path(f"/mnt/gaol/{name}"),
                )
                store.save_dataset(ds)

            manager = CoWManager(store=store)
            datasets = manager.list_datasets("test")
            assert len(datasets) == 2


# =============================================================================
# Integration Tests
# =============================================================================


class TestCoWIntegration:
    """Integration tests for CoW module."""

    def test_pool_dataset_lifecycle(self) -> None:
        """Test complete pool and dataset lifecycle with mocked backend."""
        from gaol.cow.manager import CoWManager
        from gaol.cow.store import CoWStore
        from gaol.cow.backends.btrfs import BtrfsBackend

        with tempfile.TemporaryDirectory() as tmpdir:
            store = CoWStore(store_dir=Path(tmpdir))
            manager = CoWManager(store=store)

            # Mock the backend
            mock_backend = MagicMock(spec=BtrfsBackend)
            mock_backend.name = "btrfs"
            mock_backend.is_available.return_value = True

            mock_backend.init_pool.return_value = PoolState(
                name="mypool",
                backend=CoWBackendType.BTRFS,
                mount_point=Path("/mnt/btrfs"),
                gaol_path=Path("/mnt/btrfs/gaol"),
            )

            mock_backend.create_dataset.return_value = DatasetState(
                name="data",
                pool="mypool",
                path=Path("/mnt/btrfs/gaol/data"),
            )

            mock_backend.create_snapshot.return_value = Snapshot(
                name="v1",
                dataset="data",
                pool="mypool",
            )

            mock_backend.list_snapshots.return_value = [
                Snapshot(name="v1", dataset="data", pool="mypool"),
            ]

            mock_backend.get_dataset_info.return_value = DatasetState(
                name="data",
                pool="mypool",
                path=Path("/mnt/btrfs/gaol/data"),
                snapshots=["v1"],
            )

            # Inject mocked backend
            manager._backends[CoWBackendType.BTRFS] = mock_backend

            # Create pool
            pool_config = PoolConfig(name="mypool", mount_point=Path("/mnt/btrfs"))
            pool = manager.init_pool(pool_config)
            assert pool.name == "mypool"

            # Create dataset
            ds_config = DatasetConfig(name="data", pool="mypool")
            dataset = manager.create_dataset(ds_config)
            assert dataset.name == "data"

            # Create snapshot
            snap = manager.create_snapshot("mypool", "data", "v1")
            assert snap.name == "v1"

            # List snapshots
            snapshots = manager.list_snapshots("mypool", "data")
            assert len(snapshots) == 1

            # Verify persistence
            assert store.pool_exists("mypool")
            assert store.dataset_exists("mypool", "data")

    def test_module_exports(self) -> None:
        """Test that all expected exports are available."""
        from gaol import cow

        # Manager
        assert hasattr(cow, "CoWManager")
        assert hasattr(cow, "get_cow_manager")

        # Store
        assert hasattr(cow, "CoWStore")

        # Models
        assert hasattr(cow, "CoWBackendType")
        assert hasattr(cow, "PoolConfig")
        assert hasattr(cow, "PoolState")
        assert hasattr(cow, "PoolStatus")
        assert hasattr(cow, "DatasetConfig")
        assert hasattr(cow, "DatasetState")
        assert hasattr(cow, "Snapshot")
        assert hasattr(cow, "CloneConfig")
        assert hasattr(cow, "SendOptions")

        # Exceptions
        assert hasattr(cow, "CoWError")
        assert hasattr(cow, "PoolNotFoundError")
        assert hasattr(cow, "PoolExistsError")
        assert hasattr(cow, "DatasetNotFoundError")
        assert hasattr(cow, "DatasetExistsError")
        assert hasattr(cow, "SnapshotNotFoundError")
        assert hasattr(cow, "SnapshotExistsError")
        assert hasattr(cow, "BackendNotAvailableError")
