# test_storage_backends.py
"""Tests for storage backend enhancements.

This module tests the new storage backend methods:
- get_etag: Get file checksum/ETag for change detection
- rmtree: Recursive directory deletion
- list_files: List files in directory with pattern matching
"""

import tempfile
from pathlib import Path

import pytest

from earthcatalog.storage_backends import LocalStorage, get_storage_backend


class TestLocalStorageEnhancements:
    """Test LocalStorage new methods."""

    @pytest.fixture
    def temp_dir(self):
        """Create a temporary directory for testing."""
        with tempfile.TemporaryDirectory() as tmpdir:
            yield tmpdir

    @pytest.fixture
    def local_storage(self, temp_dir):
        """Create a LocalStorage instance."""
        return LocalStorage(temp_dir)

    def test_get_etag_returns_hash_for_existing_file(self, local_storage, temp_dir):
        """get_etag should return a hash string for existing files."""
        # Create a test file
        test_file = Path(temp_dir) / "test.txt"
        test_file.write_text("hello world")

        etag = local_storage.get_etag(str(test_file))

        assert etag is not None
        assert isinstance(etag, str)
        assert len(etag) > 0

    def test_get_etag_returns_none_for_missing_file(self, local_storage, temp_dir):
        """get_etag should return None for non-existent files."""
        missing_file = Path(temp_dir) / "nonexistent.txt"

        etag = local_storage.get_etag(str(missing_file))

        assert etag is None

    def test_get_etag_different_content_different_hash(self, local_storage, temp_dir):
        """Different file contents should produce different ETags."""
        file1 = Path(temp_dir) / "file1.txt"
        file2 = Path(temp_dir) / "file2.txt"
        file1.write_text("content one")
        file2.write_text("content two")

        etag1 = local_storage.get_etag(str(file1))
        etag2 = local_storage.get_etag(str(file2))

        assert etag1 != etag2

    def test_get_etag_same_content_same_hash(self, local_storage, temp_dir):
        """Same file contents should produce same ETags."""
        file1 = Path(temp_dir) / "file1.txt"
        file2 = Path(temp_dir) / "file2.txt"
        file1.write_text("identical content")
        file2.write_text("identical content")

        etag1 = local_storage.get_etag(str(file1))
        etag2 = local_storage.get_etag(str(file2))

        assert etag1 == etag2

    def test_rmtree_removes_directory_recursively(self, local_storage, temp_dir):
        """rmtree should remove directory and all contents."""
        # Create nested directory structure
        nested_dir = Path(temp_dir) / "parent" / "child"
        nested_dir.mkdir(parents=True)
        (nested_dir / "file.txt").write_text("content")
        (Path(temp_dir) / "parent" / "sibling.txt").write_text("other")

        parent_path = str(Path(temp_dir) / "parent")
        local_storage.rmtree(parent_path)

        assert not Path(parent_path).exists()

    def test_rmtree_handles_missing_directory(self, local_storage, temp_dir):
        """rmtree should not raise error for non-existent directory."""
        missing_dir = Path(temp_dir) / "does_not_exist"

        # Should not raise
        local_storage.rmtree(str(missing_dir))

    def test_rmtree_handles_empty_directory(self, local_storage, temp_dir):
        """rmtree should handle empty directories."""
        empty_dir = Path(temp_dir) / "empty"
        empty_dir.mkdir()

        local_storage.rmtree(str(empty_dir))

        assert not empty_dir.exists()

    def test_list_files_returns_matching_files(self, local_storage, temp_dir):
        """list_files should return files matching the pattern."""
        # Create test files
        (Path(temp_dir) / "data1.parquet").write_text("data")
        (Path(temp_dir) / "data2.parquet").write_text("data")
        (Path(temp_dir) / "readme.txt").write_text("text")

        files = local_storage.list_files(temp_dir, "*.parquet")

        assert len(files) == 2
        assert all(".parquet" in f for f in files)

    def test_list_files_returns_empty_for_no_matches(self, local_storage, temp_dir):
        """list_files should return empty list when no files match."""
        (Path(temp_dir) / "data.txt").write_text("data")

        files = local_storage.list_files(temp_dir, "*.parquet")

        assert files == []

    def test_list_files_handles_missing_directory(self, local_storage, temp_dir):
        """list_files should return empty list for non-existent directory."""
        missing_dir = Path(temp_dir) / "nonexistent"

        files = local_storage.list_files(str(missing_dir), "*")

        assert files == []

    def test_list_files_recursive_with_glob_pattern(self, local_storage, temp_dir):
        """list_files with ** should match files recursively."""
        # Create nested structure
        subdir = Path(temp_dir) / "subdir"
        subdir.mkdir()
        (Path(temp_dir) / "root.parquet").write_text("data")
        (subdir / "nested.parquet").write_text("data")

        files = local_storage.list_files(temp_dir, "**/*.parquet")

        # Should find both files
        assert len(files) >= 1  # At least the nested one with **

    def test_list_files_default_pattern_matches_all(self, local_storage, temp_dir):
        """list_files with default pattern should match all files."""
        (Path(temp_dir) / "file1.txt").write_text("data")
        (Path(temp_dir) / "file2.json").write_text("data")

        files = local_storage.list_files(temp_dir)

        assert len(files) == 2

    def test_list_dirs_returns_subdirectories(self, local_storage, temp_dir):
        """list_dirs should return immediate subdirectories."""
        # Create subdirectories
        (Path(temp_dir) / "subdir1").mkdir()
        (Path(temp_dir) / "subdir2").mkdir()
        # Create a file (should not be included)
        (Path(temp_dir) / "file.txt").write_text("data")

        dirs = local_storage.list_dirs(temp_dir)

        assert len(dirs) == 2
        dir_names = [Path(d).name for d in dirs]
        assert "subdir1" in dir_names
        assert "subdir2" in dir_names

    def test_list_dirs_returns_empty_for_no_subdirs(self, local_storage, temp_dir):
        """list_dirs should return empty list when no subdirectories exist."""
        # Create only files
        (Path(temp_dir) / "file1.txt").write_text("data")
        (Path(temp_dir) / "file2.txt").write_text("data")

        dirs = local_storage.list_dirs(temp_dir)

        assert dirs == []

    def test_list_dirs_handles_missing_directory(self, local_storage, temp_dir):
        """list_dirs should return empty list for non-existent directory."""
        missing_dir = Path(temp_dir) / "nonexistent"

        dirs = local_storage.list_dirs(str(missing_dir))

        assert dirs == []

    def test_list_dirs_does_not_recurse(self, local_storage, temp_dir):
        """list_dirs should only return immediate children, not nested."""
        # Create nested structure
        (Path(temp_dir) / "parent").mkdir()
        (Path(temp_dir) / "parent" / "child").mkdir()

        dirs = local_storage.list_dirs(temp_dir)

        assert len(dirs) == 1
        assert Path(dirs[0]).name == "parent"


class TestGetStorageBackend:
    """Test the storage backend factory function."""

    def test_local_path_returns_local_storage(self):
        """Local paths should return LocalStorage."""
        storage = get_storage_backend("/tmp/test")
        assert isinstance(storage, LocalStorage)

    def test_relative_path_returns_local_storage(self):
        """Relative paths should return LocalStorage."""
        storage = get_storage_backend("./catalog")
        assert isinstance(storage, LocalStorage)


# S3 tests would require moto or similar mocking
class TestS3StorageEnhancements:
    """Test S3Storage new methods (requires mocking or live S3)."""

    @pytest.mark.skip(reason="Requires S3 credentials or mocking")
    def test_get_etag_returns_s3_etag(self):
        """S3 get_etag should return the S3 ETag header value."""
        pass

    @pytest.mark.skip(reason="Requires S3 credentials or mocking")
    def test_rmtree_deletes_s3_prefix(self):
        """S3 rmtree should delete all objects with the given prefix."""
        pass

    @pytest.mark.skip(reason="Requires S3 credentials or mocking")
    def test_list_files_lists_s3_objects(self):
        """S3 list_files should list objects matching pattern."""
        pass

    @pytest.mark.skip(reason="Requires S3 credentials or mocking")
    def test_list_dirs_lists_s3_prefixes(self):
        """S3 list_dirs should list directory-like prefixes."""
        pass
