#!/usr/bin/env python3
"""
Storage download regression tests.

These checks keep download-path behavior deterministic by using fake storage and
mocked boto clients rather than live object stores.
"""

from __future__ import annotations

import asyncio
import os
import sys
import tempfile
from pathlib import Path
from unittest.mock import Mock, patch

from test_support import install_sdk_dependency_stubs

install_sdk_dependency_stubs()

sys.path.insert(0, str(Path(__file__).parent.parent))

from sandai.operator.processor import BatchProcessor
from sandai.operator.storage import FakeStorageProvider, MinIOStorageProvider


def test_fake_storage_download_writes_local_file() -> None:
    provider = FakeStorageProvider()

    with tempfile.TemporaryDirectory() as temp_dir:
        download_path = Path(temp_dir) / "downloaded_fake.txt"
        result_path = asyncio.run(
            provider.download_file("fake://test-bucket/test-key.txt", str(download_path))
        )

        assert result_path == str(download_path)
        assert download_path.exists()
        content = download_path.read_text(encoding="utf-8")
        assert "Mock file content" in content
        assert "fake://test-bucket/test-key.txt" in content


def test_minio_download_uses_boto_client_without_network() -> None:
    with patch("boto3.client") as mock_boto3:
        mock_s3_client = Mock()
        mock_boto3.return_value = mock_s3_client

        def mock_download_file(bucket: str, key: str, local_path: str) -> None:
            Path(local_path).write_text(
                f"Mock S3 content for s3://{bucket}/{key}",
                encoding="utf-8",
            )

        mock_s3_client.download_file.side_effect = mock_download_file
        provider = MinIOStorageProvider()

        with tempfile.TemporaryDirectory() as temp_dir:
            download_path = Path(temp_dir) / "downloaded_s3.txt"
            mock_s3_client.head_bucket.return_value = {}
            result_path = asyncio.run(
                provider.download_file("s3://test-bucket/test-key.txt", str(download_path))
            )

            assert result_path == str(download_path)
            assert download_path.exists()
            assert "Mock S3 content" in download_path.read_text(encoding="utf-8")
            mock_s3_client.head_bucket.assert_called_once_with(Bucket=provider.config.bucket_name)
            mock_s3_client.download_file.assert_called_once_with(
                "test-bucket", "test-key.txt", str(download_path)
            )


def test_download_error_handling_and_directory_creation() -> None:
    provider = FakeStorageProvider()

    async def invalid_path_raises() -> bool:
        try:
            await provider.download_file("fake://bucket/key.txt", "/dev/null/invalid/path.txt")
            return False
        except Exception:
            return True

    assert asyncio.run(invalid_path_raises()) is True

    with tempfile.TemporaryDirectory() as temp_dir:
        nested_path = Path(temp_dir) / "nested" / "deep" / "path" / "test.txt"
        result_path = asyncio.run(
            provider.download_file("fake://bucket/key.txt", str(nested_path))
        )
        assert result_path == str(nested_path)
        assert nested_path.exists()
        assert nested_path.parent.exists()


def test_processor_uri_to_path_supports_file_and_fake_uris() -> None:
    with patch.dict(os.environ, {"SANDAI_OPERATOR_STORAGE_TYPE": "fake"}):
        processor = BatchProcessor("test-operator", "v1", ignore_nongil_check=True)
        processor._setup_temp_directory()

        with tempfile.TemporaryDirectory() as temp_dir:
            source_path = Path(temp_dir) / "input.txt"
            source_path.write_text("Test file content", encoding="utf-8")

            file_result = processor._uri_to_path(f"file://{source_path}", "task-1", "inputs")
            fake_result = processor._uri_to_path("fake://bucket/key.txt", "task-2", "inputs")

            assert Path(file_result).exists()
            assert Path(fake_result).exists()
            assert Path(file_result).read_text(encoding="utf-8") == "Test file content"
            assert "Mock file content" in Path(fake_result).read_text(encoding="utf-8")

            Path(file_result).unlink()
            Path(fake_result).unlink()


def test_uri_protocol_parsing_examples_are_well_formed() -> None:
    test_cases = [
        ("s3://bucket/key.txt", "bucket", "key.txt"),
        ("oss://my-bucket/path/to/file.txt", "my-bucket", "path/to/file.txt"),
        ("fake://test/nested/path/file.txt", "test", "nested/path/file.txt"),
    ]

    for uri, expected_bucket, expected_key in test_cases:
        protocol, remainder = uri.split("://", 1)
        bucket, key = remainder.split("/", 1)
        assert protocol in {"s3", "oss", "fake"}
        assert bucket == expected_bucket
        assert key == expected_key


def main() -> int:
    tests = [
        ("fake_download", test_fake_storage_download_writes_local_file),
        ("minio_download", test_minio_download_uses_boto_client_without_network),
        ("error_handling", test_download_error_handling_and_directory_creation),
        ("processor_uri_to_path", test_processor_uri_to_path_supports_file_and_fake_uris),
        ("uri_parsing", test_uri_protocol_parsing_examples_are_well_formed),
    ]

    passed = 0
    failed = 0

    print("Storage download regression tests")
    print("=" * 60)
    for name, test_func in tests:
        try:
            test_func()
            print(f"PASS {name}")
            passed += 1
        except Exception as exc:
            print(f"FAIL {name}: {exc}")
            import traceback

            traceback.print_exc()
            failed += 1
    print("=" * 60)
    print(f"passed={passed} failed={failed}")
    return 0 if failed == 0 else 1


if __name__ == "__main__":
    raise SystemExit(main())
