import pytest
import shutil
from aleph_blobstorage.adapters import LocalFilesystemAdaptor
from aleph_blobstorage.valueobjects import VOLocalBlobURI


@pytest.fixture
def temp_storage(tmp_path):
    """Fixture providing temporary storage directory"""
    yield tmp_path
    shutil.rmtree(tmp_path)


@pytest.fixture
def storage(temp_storage):
    """Fixture providing LocalFilesystemAdaptor instance"""
    return LocalFilesystemAdaptor(temp_storage)


@pytest.fixture
def tmp_file(tmp_path):
    """Temp file to test uploads"""
    test_file = tmp_path / "test_file.py"
    test_file.write_text("This is a test blob.")

    return test_file


# ...existing code...


class TestUpload:
    def test_file_upload(self, storage, tmp_file):
        """Test uploading file to storage"""
        versioned_uri = VOLocalBlobURI(logical_name="test.py", logical_path="test")
        lifecycle = storage.upload_from_file(
            destination=versioned_uri,
            source_path=tmp_file,
        )
        assert len(lifecycle.versions) == 1
        # print(storage._build_storage_path(lifecycle.versions[0].uri))

    def test_memory_upload(self, storage):
        """Test uploading bytes to storage"""
        versioned_uri = VOLocalBlobURI(logical_name="mem.txt", logical_path="mem")
        content = b"memory upload test"
        lifecycle = storage.upload_from_memory(
            destination=versioned_uri,
            content=content,
        )
        assert len(lifecycle.versions) == 1
        downloaded = storage.download_to_memory(versioned_uri, max_size_bytes=100)
        assert downloaded == content

    def test_upload_with_tags(self, storage, tmp_path):
        """Test uploading with custom metadata tags"""
        test_file = tmp_path / "tagged.txt"
        test_file.write_text("tagged upload")
        versioned_uri = VOLocalBlobURI(logical_name="tagged.txt", logical_path="meta")
        tags = {"project": "aleph", "env": "test"}
        lifecycle = storage.upload_from_file(
            destination=versioned_uri,
            source_path=test_file,
            tags=tags,
        )
        meta = lifecycle.versions[0].tags
        assert meta.get("project") == "aleph"
        assert meta.get("env") == "test"


class TestDownload:
    def test_download_to_memory(self, storage, tmp_path):
        """Test downloading blob to memory"""
        test_file = tmp_path / "mem.txt"
        test_file.write_text("download test")
        versioned_uri = VOLocalBlobURI(logical_name="mem.txt", logical_path="dl")
        storage.upload_from_file(destination=versioned_uri, source_path=test_file)
        data = storage.download_to_memory(versioned_uri, max_size_bytes=100)
        assert data == b"download test"

    def test_download_size_limit(self, storage, tmp_path):
        """Test max size limit during download"""
        test_file = tmp_path / "big.txt"
        test_file.write_text("x" * 200)
        versioned_uri = VOLocalBlobURI(logical_name="big.txt", logical_path="dl")
        storage.upload_from_file(destination=versioned_uri, source_path=test_file)
        with pytest.raises(ValueError):
            storage.download_to_memory(versioned_uri, max_size_bytes=10)

    def test_download_missing(self, storage):
        """Test downloading non-existent blob"""
        versioned_uri = VOLocalBlobURI(logical_name="missing.txt", logical_path="none")
        with pytest.raises(FileNotFoundError):
            storage.download_to_memory(versioned_uri, max_size_bytes=100)

    def test_download_to_file(self, storage, tmp_path):
        """Test backend-specific file download implementation"""
        # Upload a file
        content = "backend file download test"
        src_file = tmp_path / "src.txt"
        src_file.write_text(content)
        versioned_uri = VOLocalBlobURI(logical_name="src.txt", logical_path="dl")
        storage.upload_from_file(destination=versioned_uri, source_path=src_file)

        # Download to a new location
        dest_file = tmp_path / "dest.txt"
        storage.download_to_file(versioned_uri, dest_file)
        assert dest_file.exists()
        assert dest_file.read_text() == content

    def test_download_stream(self, storage, tmp_path):
        """Test backend-specific streaming download implementation"""
        src_file = tmp_path / "stream.txt"
        content = "streamed content for backend test"
        src_file.write_text(content)
        versioned_uri = VOLocalBlobURI(logical_name="stream.txt", logical_path="dl")
        storage.upload_from_file(destination=versioned_uri, source_path=src_file)

        # Download as stream
        chunks = list(storage.download_as_stream(versioned_uri, chunk_size=8))
        reconstructed = b"".join(chunks)
        assert reconstructed == content.encode()


class TestVersioning:
    def test_version_resolution(self, storage, tmp_path):
        """Test resolving latest version"""
        test_file = tmp_path / "ver.txt"
        test_file.write_text("v1")
        uri = VOLocalBlobURI(logical_name="ver.txt", logical_path="ver")
        lifecycle = storage.upload_from_file(destination=uri, source_path=test_file)
        latest_uri = VOLocalBlobURI(
            logical_name="ver.txt", logical_path="ver", version="latest"
        )
        resolved = storage._resolve_versioned_uri(latest_uri)
        assert resolved.version == lifecycle.versions[-1].uri.version

    def test_version_listing(self, storage, tmp_path):
        """Test listing all versions of a blob"""
        test_file = tmp_path / "multi.txt"
        test_file.write_text("v1")
        uri = VOLocalBlobURI(logical_name="multi.txt", logical_path="ver")
        storage.upload_from_file(destination=uri, source_path=test_file)
        test_file.write_text("v2")
        storage.upload_from_file(destination=uri, source_path=test_file)
        lifecycle = storage.get_lifecycle_entity(uri)
        assert len(lifecycle.versions) >= 1


class TestDelete:
    def test_delete_blob(self, storage, tmp_path):
        """Test deleting a blob and all versions"""
        test_file = tmp_path / "del.txt"
        test_file.write_text("delete me")
        uri = VOLocalBlobURI(logical_name="del.txt", logical_path="del")
        storage.upload_from_file(destination=uri, source_path=test_file)
        storage.delete(uri)
        assert not storage._check_blob_exists(uri)
        with pytest.raises(FileNotFoundError):
            storage.get_lifecycle_entity(uri)

    def test_delete_missing(self, storage):
        """Test deleting non-existent blob"""
        uri = VOLocalBlobURI(logical_name="missing.txt", logical_path="none")
        with pytest.raises(FileNotFoundError):
            storage.delete(uri)

    def test_delete_registry_update(self, storage, tmp_path):
        """Test registry updates after deletion"""
        test_file = tmp_path / "reg.txt"
        test_file.write_text("registry")
        uri = VOLocalBlobURI(logical_name="reg.txt", logical_path="reg")
        storage.upload_from_file(destination=uri, source_path=test_file)
        storage.delete(uri)
        registry = storage._load_registry()
        key = f"reg/reg.txt"
        assert key not in registry


class TestRegistry:
    def test_registry_creation(self, temp_storage):
        """Test registry file creation"""
        adaptor = LocalFilesystemAdaptor(temp_storage)
        assert adaptor.registry_path.exists()

    def test_registry_loading(self, storage):
        """Test loading registry data"""
        registry = storage._load_registry()
        assert isinstance(registry, dict)

    def test_registry_atomic_save(self, storage):
        """Test atomic registry updates"""
        registry = {
            "test/blob.txt": [
                {"version": "v1", "size_bytes": 1, "created_at": "now", "tags": {}}
            ]
        }
        storage._save_registry(registry)
        loaded = storage._load_registry()
        assert loaded == registry


class TestPathHandling:
    def test_path_validation(self, storage):
        """Test path validation security"""
        with pytest.raises(ValueError):
            storage._validate_path("../badpath")
        with pytest.raises(ValueError):
            storage._validate_path("/abs/path")
        with pytest.raises(ValueError):
            storage._validate_path("bad\\path")

    def test_path_building(self, storage):
        """Test storage path construction"""
        uri = VOLocalBlobURI(
            logical_name="file.txt", logical_path="folder", version="abc123"
        )
        path = storage._build_storage_path(uri)
        assert path == "folder/file_abc123.txt"

    def test_path_extraction(self, storage):
        """Test extracting logical info from paths"""
        uri = VOLocalBlobURI(
            logical_name="file.txt", logical_path="folder", version="abc123"
        )
        path = storage._build_storage_path(uri)
        file_path = storage.root_path / path
        file_path.parent.mkdir(parents=True, exist_ok=True)
        file_path.write_text("content")
        logical_path, logical_name, version = (
            storage._extract_logical_info_from_storage_object(file_path)
        )
        assert logical_path == "folder"
        assert logical_name == "file.txt"
        assert version == "abc123"


class TestList:
    def test_list_blobs(self, storage, tmp_path):
        uris = []
        for i in range(3):
            test_file = tmp_path / f"list{i}.txt"
            test_file.write_text(f"list {i}")
            uri = VOLocalBlobURI(logical_name=f"list{i}.txt", logical_path="list")
            lifecycle = storage.upload_from_file(destination=uri, source_path=test_file)
            uris.append(lifecycle.versions[0].uri)

        blobs_retrieved = storage.list_blobs(path="list")
        retrieved_versions = [b.version for b in blobs_retrieved]
        for uri in uris:
            assert uri.version in retrieved_versions

    def test_list_blobs_with_tags(self, storage, tmp_path):
        test_file = tmp_path / "withtag.txt"
        test_file.write_text("This has tags")
        uri = VOLocalBlobURI(logical_name="withtag.txt", logical_path="list")
        tags = {"env": "test"}
        storage.upload_from_file(destination=uri, source_path=test_file, tags=tags)

        test_file = tmp_path / "notag.txt"
        test_file.write_text("This does not have tags")
        uri2 = VOLocalBlobURI(logical_name="notag.txt", logical_path="list")
        storage.upload_from_file(destination=uri2, source_path=test_file)

        blobs_retrieved = storage.list_blobs(path="list", tags=tags)
        assert len(blobs_retrieved) == 1
        blob = blobs_retrieved[0]
        assert blob.logical_name == "withtag.txt"
        assert blob.logical_path == "list"
        assert blob.version is not None

    def test_list_blobs_empty(self, storage):
        blobs_retrieved = storage.list_blobs(path="empty")
        assert blobs_retrieved == []
