# -*- coding: utf-8 -*-
import os
from unittest.mock import Mock, AsyncMock, patch
from typing import List, Dict, Any, Tuple, Callable

import pytest
from agentscope.evaluate import Task, ACEPhone, ACEBenchmark

# Import the main module from the correct path
from evaluation.ace_bench import main as ace_main


class TestReActAgentSolution:
    """Test suite for the ReAct agent solution function"""

    @pytest.fixture
    def mock_task(self) -> Task:
        """Create a mock ACEBench task"""
        task = Mock(spec=Task)
        task.input = "Test input query"
        task.metadata = {
            "tools": self._create_mock_tools(),
            "phone": Mock(spec=ACEPhone),
        }
        return task

    @pytest.fixture
    def mock_pre_hook(self) -> Mock:
        """Create a mock pre-hook function that returns None"""

        def pre_hook_return():
            """Mock function that returns None (no modifications)"""
            return None

        mock = Mock()
        mock.__name__ = "save_logging"
        mock.side_effect = (
            pre_hook_return  # ✅ Return None to avoid parameter pollution
        )
        return mock

    def _create_mock_tools(self) -> List[Tuple[Callable, Dict[str, Any]]]:
        """Create mock tool functions with schemas"""

        def mock_tool():
            return "tool_response"

        tool_schema = {
            "type": "function",
            "function": {
                "name": "mock_tool",
                "description": "A mock tool for testing",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "param1": {"type": "string"},
                        "param2": {"type": "number"},
                    },
                    "required": ["param1"],
                },
            },
        }

        return [(mock_tool, tool_schema)]

    @pytest.mark.asyncio
    async def test_error_handling(
        self,
        mock_task: Task,
        mock_pre_hook: Mock,
    ) -> None:
        """Test error handling in the solution function"""
        with patch.dict(os.environ, {"DASHSCOPE_API_KEY": "test_key"}):
            # Mock a failure case
            with patch(
                "evaluation.ace_bench.main.Toolkit.register_tool_function",
                side_effect=Exception("Registration error"),
            ):
                with pytest.raises(Exception) as exc_info:
                    await ace_main.react_agent_solution(
                        mock_task,
                        mock_pre_hook,
                    )

                assert "Registration error" in str(exc_info.value)


class TestMainFunction:
    """Test suite for the main function"""

    @pytest.fixture
    def mock_args(self, tmpdir) -> Mock:
        """Create mock command-line arguments with temporary directories"""
        args = Mock()
        args.data_dir = str(tmpdir / "data")
        args.result_dir = str(tmpdir / "results")
        args.n_workers = 2
        return args

    @pytest.mark.asyncio
    async def test_evaluator_initialization(self, mock_args: Mock) -> None:
        """Test evaluator initialization"""
        with patch(
            "evaluation.ace_bench.main.ArgumentParser.parse_args",
            return_value=mock_args,
        ):
            with patch(
                "evaluation.ace_bench.main.RayEvaluator",
            ) as mock_evaluator_class:
                mock_evaluator = AsyncMock()
                mock_evaluator_class.return_value = mock_evaluator

                # ✅ Simulate _download_data and _load_data
                with patch(
                    "agentscope.evaluate._ace_benchmark."
                    "_ace_benchmark.ACEBenchmark._download_data",
                ):
                    with patch(
                        "agentscope.evaluate._ace_benchmark."
                        "_ace_benchmark.ACEBenchmark._load_data",
                        return_value=[],
                    ):
                        # Run main function
                        await ace_main.main()

                # Verify evaluator initialization
                mock_evaluator_class.assert_called_once()
                call_args = mock_evaluator_class.call_args[1]
                assert call_args["n_workers"] == 2
                assert isinstance(call_args["benchmark"], ACEBenchmark)
                assert call_args["benchmark"].data_dir == mock_args.data_dir

    @pytest.mark.asyncio
    async def test_evaluation_execution(self, mock_args: Mock) -> None:
        """Test evaluation execution"""
        with patch(
            "evaluation.ace_bench.main.ArgumentParser.parse_args",
            return_value=mock_args,
        ):
            with patch(
                "evaluation.ace_bench.main.RayEvaluator",
            ) as mock_evaluator_class:
                mock_evaluator = AsyncMock()
                mock_evaluator.run = AsyncMock()
                mock_evaluator_class.return_value = mock_evaluator

                # ✅ Simulate _download_data and _load_data
                with patch(
                    "agentscope.evaluate._ace_benchmark._ace_benchmark."
                    "ACEBenchmark._download_data",
                ):
                    with patch(
                        "agentscope.evaluate._ace_benchmark."
                        "_ace_benchmark.ACEBenchmark._load_data",
                        return_value=[],
                    ):
                        # Run main function
                        await ace_main.main()

                # Verify evaluation execution
                mock_evaluator.run.assert_called_once_with(
                    ace_main.react_agent_solution,
                )
