#!/usr/bin/env python3
"""
测试存储提供者

验证存储抽象和各种存储提供者的功能
"""

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

# Add the parent directory to the path to import sandai
sys.path.insert(0, str(Path(__file__).parent.parent))

from sandai.operator.storage import (
    MinIOStorageProvider, 
    OSSStorageProvider, 
    FakeStorageProvider,
    create_storage_provider,
    create_default_storage_provider,
    get_storage_type_from_env
)


def test_fake_storage_provider():
    """测试 Fake 存储提供者"""
    print("🧪 测试 Fake 存储提供者")
    
    provider = FakeStorageProvider()
    
    # 测试基本属性
    assert provider.storage_type == "fake"
    
    print(f"   存储类型: {provider.storage_type}")
    
    # 创建测试文件
    with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
        f.write("Test content for fake storage")
        test_file_path = f.name
    
    try:
        # 测试上传
        async def test_upload():
            uri, meta = await provider.upload_file(test_file_path)
            return uri, meta
        
        result_uri, meta = asyncio.run(test_upload())
        
        # 验证 URI 格式
        assert result_uri.startswith("fake://")
        assert meta["type"] == "fake"
        
        print(f"   ✅ 上传成功: {result_uri}")
        print(f"   📋 元数据: {meta['size']} bytes")
        
        # 测试下载
        with tempfile.TemporaryDirectory() as temp_dir:
            download_path = os.path.join(temp_dir, "downloaded.txt")
            
            async def test_download():
                return await provider.download_file(result_uri, download_path)
            
            downloaded_path = asyncio.run(test_download())
            
            # 验证下载结果
            assert downloaded_path == download_path
            assert os.path.exists(download_path)
            
            with open(download_path, 'r', encoding='utf-8') as f:
                content = f.read()
                assert "Mock file content" in content
            
            print(f"   ✅ 下载成功: {downloaded_path}")
        
    finally:
        # 清理测试文件
        if os.path.exists(test_file_path):
            os.unlink(test_file_path)
    
    print("✅ Fake 存储提供者测试通过")
    return True


def test_minio_storage_provider():
    """测试 MinIO 存储提供者"""
    print("\n🧪 测试 MinIO 存储提供者")
    
    # 检查是否有 boto3
    try:
        import boto3
        has_boto3 = True
    except ImportError:
        has_boto3 = False
    
    if has_boto3:
        # Mock boto3 进行测试
        with patch('boto3.client') as mock_boto3:
            mock_s3_client = Mock()
            mock_boto3.return_value = mock_s3_client
            
            # Mock head_bucket (bucket 存在)
            mock_s3_client.head_bucket.return_value = {}
            mock_s3_client.upload_file.return_value = None
            
            # 现在创建 provider（会在 __init__ 中调用 boto3.client）
            provider = MinIOStorageProvider()
            
            print(f"   存储类型: {provider.storage_type}")
            
            # 创建测试文件
            with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
                f.write("Test content for MinIO storage")
                test_file_path = f.name
            
            try:
                # 测试上传
                async def test_upload():
                    url = await provider.upload_file(test_file_path)
                    return url
                
                result_uri, meta = asyncio.run(test_upload())
                
                # 验证结果
                assert result_uri.startswith("s3://")
                assert meta["external-url"].startswith("http://localhost:9000/operators-data-exchange/processed/")
                assert meta["external-url"].endswith(".txt")
                
                # 验证 boto3 调用（在 __init__ 中调用一次）
                mock_boto3.assert_called_once()
                mock_s3_client.upload_file.assert_called_once()
                
                print(f"   ✅ 上传成功: {result_uri}")
                print(f"   🔗 外部 URL: {meta['external-url']}")
                
            finally:
                # 清理测试文件
                if os.path.exists(test_file_path):
                    os.unlink(test_file_path)
    else:
        # 测试没有 boto3 时的错误处理
        try:
            provider = MinIOStorageProvider()
            assert False, "应该在初始化时抛出 ImportError"
        except ImportError as e:
            if "boto3" in str(e):
                print(f"   ✅ 正确处理 boto3 缺失的情况")
            else:
                raise
    
    print("✅ MinIO 存储提供者测试通过")
    return True

def test_storage_provider_factory():
    """测试存储提供者工厂函数"""
    print("\n🧪 测试存储提供者工厂函数")
    
    # 测试指定类型创建
    fake_provider = create_storage_provider("fake")
    assert fake_provider.storage_type == "fake"
    print(f"   ✅ 创建 fake 存储提供者: {fake_provider.storage_type}")
    
    minio_provider = create_storage_provider("minio")
    assert minio_provider.storage_type == "minio"
    print(f"   ✅ 创建 minio 存储提供者: {minio_provider.storage_type}")
    
    # 测试无效类型
    try:
        create_storage_provider("invalid")
        assert False, "应该抛出 ValueError"
    except ValueError:
        print(f"   ✅ 正确处理无效存储类型")
    
    # 测试自动选择（会根据配置自动选择可用的存储）
    auto_provider = create_storage_provider()
    print(f"   ✅ 自动选择存储提供者: {auto_provider.storage_type}")
    # 不强制要求是 fake，因为可能有其他存储配置可用
    
    print("✅ 存储提供者工厂函数测试通过")
    return True


def test_environment_storage_type():
    """测试环境变量存储类型配置"""
    print("\n🧪 测试环境变量存储类型配置")
    
    # 清除环境变量进行测试
    with patch.dict(os.environ, {}, clear=True):
        storage_type = get_storage_type_from_env()
        assert storage_type is None
        print(f"   ✅ 默认情况返回 None")
    
    # 测试设置环境变量
    test_cases = [
        ("fake", "fake"),
        ("minio", "minio"),
        ("FAKE", "fake"),  # 测试大小写不敏感
        ("MinIO", "minio"),
    ]
    
    for env_value, expected in test_cases:
        with patch.dict(os.environ, {"SANDAI_OPERATOR_STORAGE_TYPE": env_value}):
            storage_type = get_storage_type_from_env()
            assert storage_type == expected
            print(f"   ✅ 环境变量 '{env_value}' -> '{expected}'")
    
    # 测试无效值
    with patch.dict(os.environ, {"SANDAI_OPERATOR_STORAGE_TYPE": "invalid"}):
        try:
            get_storage_type_from_env()
            assert False, "应该抛出 ValueError"
        except ValueError:
            print(f"   ✅ 正确处理无效环境变量值")
    
    print("✅ 环境变量存储类型配置测试通过")
    return True


def test_default_storage_provider():
    """测试默认存储提供者创建"""
    print("\n🧪 测试默认存储提供者创建")
    
    # 测试默认情况（会根据配置自动选择）
    default_provider = create_default_storage_provider()
    print(f"   ✅ 默认选择: {default_provider.storage_type}")
    # 不强制要求是 fake，因为可能有其他存储配置可用
    
    # 测试环境变量指定
    test_cases = ["fake", "minio"]
    
    for storage_type in test_cases:
        with patch.dict(os.environ, {"SANDAI_OPERATOR_STORAGE_TYPE": storage_type}):
            provider = create_default_storage_provider()
            assert provider.storage_type == storage_type
            print(f"   ✅ 环境变量指定 '{storage_type}': {provider.storage_type}")
    
    print("✅ 默认存储提供者创建测试通过")
    return True


def test_storage_provider_integration():
    """测试存储提供者集成"""
    print("\n🧪 测试存储提供者集成")
    
    # 创建测试文件
    with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
        f.write("Integration test content")
        test_file_path = f.name
    
    try:
        # 测试不同存储类型的切换
        storage_types = ["fake", "minio"]
        for storage_type in storage_types:
            envs = {"SANDAI_OPERATOR_STORAGE_TYPE": storage_type}
            with patch.dict(os.environ, envs):
                provider = create_default_storage_provider()
                
                if storage_type == "fake":
                    # Fake 存储应该总是能工作
                    async def test_upload():
                        return await provider.upload_file(test_file_path)
                    
                    result_uri, meta = asyncio.run(test_upload())
                    assert result_uri.startswith(("http", "fake://"))
                    print(f"   ✅ {storage_type} 存储集成测试通过: {result_uri[:50]}...")
                else:
                    # 其他存储可能因为依赖缺失而失败，这是正常的
                    print(f"   ✅ {storage_type} 存储提供者创建成功")
        
    finally:
        # 清理测试文件
        if os.path.exists(test_file_path):
            os.unlink(test_file_path)
    
    print("✅ 存储提供者集成测试通过")
    return True


def run_all_tests():
    """运行所有存储提供者测试"""
    print("🚀 开始测试存储提供者")
    print("=" * 60)
    
    tests = [
        test_fake_storage_provider,
        test_minio_storage_provider,
        test_storage_provider_factory,
        test_environment_storage_type,
        test_default_storage_provider,
        test_storage_provider_integration,
    ]
    
    passed = 0
    failed = 0
    
    for test in tests:
        try:
            if test():
                passed += 1
            else:
                failed += 1
        except Exception as e:
            print(f"❌ 测试失败: {test.__name__}")
            print(f"   错误: {e}")
            import traceback
            traceback.print_exc()
            failed += 1
    
    print("\n" + "=" * 60)
    print(f"📊 测试结果: {passed} 通过, {failed} 失败")
    
    if failed == 0:
        print("🎉 所有存储提供者测试通过!")
        print("\n📋 功能总结:")
        print("   ✅ 抽象存储接口设计")
        print("   ✅ MinIO 存储提供者")
        print("   ✅ Fake 存储提供者")
        print("   ✅ 存储提供者工厂")
        print("   ✅ 环境变量配置")
        print("   ✅ 自动回退机制")
        return True
    else:
        print("💥 部分测试失败，请检查实现")
        return False


if __name__ == '__main__':
    success = run_all_tests()
    sys.exit(0 if success else 1)