"""测试RoboDockActions类."""

import pytest
import threading
from unittest.mock import Mock, patch, MagicMock
from pathlib import Path
from robodock_sdk.actions import RoboDockActions


@pytest.fixture
def actions():
    """创建RoboDockActions实例用于测试."""
    RoboDockActions.wait_for_uploads()
    with patch.dict(
        "os.environ",
        {
            "S3_BUCKET_NAME": "robodock",
            "S3_REGION_NAME": "us-east-1",
        },
        clear=False,
    ):
        with patch('boto3.client'):
            instance = RoboDockActions(
                s3_endpoint="http://test:9000",
                s3_access_key="test",
                s3_secret_key="test",
                api_url="http://test:8080"
            )
            yield instance
    RoboDockActions.wait_for_uploads()


def test_init(actions):
    """测试初始化."""
    assert actions.server_url == "http://test:8080"
    assert actions.bucket_name == "robodock"


def test_init_uses_env_fallbacks():
    """未传入参数时应使用环境变量回退值."""
    with patch.dict(
        "os.environ",
        {
            "S3_ENDPOINT_URL": "http://env:9000",
            "AWS_ACCESS_KEY_ID": "env-ak",
            "AWS_SECRET_ACCESS_KEY": "env-sk",
            "ROBOCK_SERVER_URL": "http://env:8080",
            "S3_BUCKET_NAME": "env-bucket",
            "S3_REGION_NAME": "env-region",
        },
    ):
        with patch("boto3.client") as mock_client:
            RoboDockActions.wait_for_uploads()
            actions = RoboDockActions()
            mock_client.assert_called_once()
            _, kwargs = mock_client.call_args
            assert kwargs["endpoint_url"] == "http://env:9000"
            assert kwargs["aws_access_key_id"] == "env-ak"
            assert kwargs["aws_secret_access_key"] == "env-sk"
            assert kwargs["region_name"] == "env-region"
            assert actions.server_url == "http://env:8080"
            assert actions.bucket_name == "env-bucket"


def test_get_dataset_info_success(actions):
    """测试获取数据集信息成功."""
    with patch('requests.get') as mock_get:
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"data": {"uuid": "test-uuid"}}
        mock_get.return_value = mock_response
        
        result = actions.get_dataset_info("test-uuid")
        
        assert result == {"data": {"uuid": "test-uuid"}}
        mock_get.assert_called_once()


def test_get_dataset_info_failure(actions):
    """测试获取数据集信息失败."""
    with patch('requests.get') as mock_get:
        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.text = "Not found"
        mock_get.return_value = mock_response
        
        with pytest.raises(Exception) as exc_info:
            actions.get_dataset_info("test-uuid")
        
        assert "Failed to get dataset info" in str(exc_info.value)


def test_update_run_success(actions):
    """测试更新运行状态成功."""
    with patch('requests.patch') as mock_patch:
        mock_response = Mock()
        mock_response.status_code = 200
        mock_patch.return_value = mock_response
        
        result = actions.update_run("test-run", {"status": "success"})
        
        assert result == 200
        mock_patch.assert_called_once()


def test_update_run_failure(actions):
    """测试更新运行状态失败."""
    with patch('requests.patch') as mock_patch:
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.text = "Bad request"
        mock_patch.return_value = mock_response
        
        with pytest.raises(Exception) as exc_info:
            actions.update_run("test-run", {"status": "success"})
        
        assert "Failed to update run" in str(exc_info.value)


def test_upload_data_async_executes(actions):
    """测试异步上传会执行并返回Future."""
    done_event = threading.Event()

    def _fake_upload(outdir_path, upload_path):
        done_event.set()
        return "s3://bucket/key"

    with patch.dict("os.environ", {"UPLOAD_MAX_WORKERS": "1", "UPLOAD_MAX_PENDING": "1"}):
        with patch.object(actions, "upload_data", side_effect=_fake_upload) as mock_upload:
            future = actions.upload_data_async("/tmp/out", "models/test")
            assert future is not None
            future.result(timeout=2)
            assert done_event.is_set()
            mock_upload.assert_called_once()


def test_upload_data_async_queue_full(actions):
    """测试异步上传队列满时返回None."""
    block_event = threading.Event()

    def _blocking_upload(outdir_path, upload_path):
        block_event.wait(timeout=2)
        return "s3://bucket/key"

    with patch.dict("os.environ", {"UPLOAD_MAX_WORKERS": "1", "UPLOAD_MAX_PENDING": "1"}):
        with patch.object(actions, "upload_data", side_effect=_blocking_upload):
            first = actions.upload_data_async("/tmp/out1", "models/test1")
            assert first is not None
            second = actions.upload_data_async("/tmp/out2", "models/test2")
            assert second is None
            block_event.set()
            first.result(timeout=2)


def test_wait_for_uploads_clears_executor(actions):
    """测试等待异步上传会清理执行器."""
    done_event = threading.Event()

    def _fake_upload(outdir_path, upload_path):
        done_event.set()
        return "s3://bucket/key"

    with patch.dict("os.environ", {"UPLOAD_MAX_WORKERS": "1", "UPLOAD_MAX_PENDING": "1"}):
        with patch.object(actions, "upload_data", side_effect=_fake_upload):
            future = actions.upload_data_async("/tmp/out", "models/test")
            assert future is not None
            RoboDockActions.wait_for_uploads()
            assert done_event.is_set()
            assert RoboDockActions._upload_executor is None
            assert RoboDockActions._upload_pending_slots is None
