"""
扩展性验证测试。

验证无状态模式和多实例水平扩展能力。
"""
import pytest
import asyncio
import httpx
import time
from tests.conftest import ServerInstance


class TestStatelessMode:
    """测试无状态 HTTP 模式验证。"""

    @pytest.mark.asyncio
    async def test_stateless_server_startup(
        self, server_instance: ServerInstance, http_client: httpx.AsyncClient
    ) -> None:
        """
        验证服务器在 stateless=True 配置下启动。

        Given: 使用 http 传输模式启动服务器（stateless=True）
        When: 向服务器发送 HEAD 请求
        Then: 服务器应该正常响应
        """
        # 等待服务器启动
        await self._wait_for_server(server_instance.base_url, http_client)

        # 验证服务器可访问
        response = await http_client.head(f"{server_instance.base_url}/mcp")
        assert response.status_code in [200, 405], \
            f"服务器应返回 200 或 405，实际返回 {response.status_code}"

    @pytest.mark.asyncio
    async def test_stateless_mode_configuration(
        self, server_instance: ServerInstance, http_client: httpx.AsyncClient
    ) -> None:
        """
        验证无状态模式配置生效。

        Given: 服务器以 stateless=True 模式运行
        When: 检查服务器响应头和状态
        Then: 无状态模式配置应该生效（服务器响应请求不依赖会话状态）
        """
        await self._wait_for_server(server_instance.base_url, http_client)

        # 发送多个独立请求
        responses = []
        for _ in range(3):
            response = await http_client.head(f"{server_instance.base_url}/mcp")
            responses.append(response.status_code)

        # 验证所有请求都成功（无状态模式不应影响请求处理）
        for status in responses:
            assert status in [200, 405], \
                f"无状态模式应正确处理所有请求，实际返回 {status}"

    @pytest.mark.asyncio
    async def test_stateless_server_responds_to_requests(
        self, server_instance: ServerInstance, http_client: httpx.AsyncClient
    ) -> None:
        """
        验证服务器能响应请求。

        Given: 无状态模式服务器运行中
        When: 向服务器发送多个并发请求
        Then: 服务器应该正确响应所有请求
        """
        await self._wait_for_server(server_instance.base_url, http_client)

        # 并发发送 10 个请求
        tasks = [
            http_client.head(f"{server_instance.base_url}/mcp")
            for _ in range(10)
        ]
        responses = await asyncio.gather(*tasks, return_exceptions=True)

        # 验证所有请求都成功
        successful_responses = [
            r for r in responses
            if not isinstance(r, BaseException) and r.status_code in [200, 405]
        ]
        assert len(successful_responses) == 10, \
            f"服务器应正确响应 10 个并发请求，实际成功 {len(successful_responses)} 个"

    async def _wait_for_server(
        self, base_url: str, client: httpx.AsyncClient, max_retries: int = 15
    ) -> None:
        """
        等待服务器启动完成。

        Args:
            base_url: 服务器基础 URL
            client: httpx 异步客户端
            max_retries: 最大重试次数
        """
        for _ in range(max_retries):
            try:
                response = await client.head(f"{base_url}/mcp", timeout=1.0)
                if response.status_code in [200, 405]:
                    return
            except (httpx.ConnectError, httpx.TimeoutException):
                time.sleep(0.5)
        raise AssertionError(f"服务器未能在 {max_retries * 0.5} 秒内启动")


class TestMultiInstanceHorizontalScaling:
    """测试多实例水平扩展验证。"""

    @pytest.mark.asyncio
    async def test_multiple_instances_startup(
        self, multiple_server_instances: list[ServerInstance]
    ) -> None:
        """
        验证多个服务器实例可以在不同端口启动。

        Given: 启动 3 个服务器实例在不同端口
        When: 检查每个实例的进程状态
        Then: 所有实例都应该正常运行
        """
        # 验证所有服务器实例都已启动
        for server in multiple_server_instances:
            assert server.is_alive(), \
                f"端口 {server.port} 的服务器实例未正常运行"

    @pytest.mark.asyncio
    async def test_concurrent_requests_to_all_instances(
        self, multiple_server_instances: list[ServerInstance], http_client: httpx.AsyncClient
    ) -> None:
        """
        验证并发向所有实例发送请求都能正常响应。

        Given: 3 个服务器实例运行在不同端口
        When: 向所有实例并发发送请求
        Then: 所有实例都应该正常响应
        """
        # 等待所有服务器启动
        for server in multiple_server_instances:
            await self._wait_for_server(server.base_url, http_client)

        # 向每个实例发送请求
        tasks = [
            http_client.head(f"{server.base_url}/mcp")
            for server in multiple_server_instances
        ]
        responses = await asyncio.gather(*tasks, return_exceptions=True)

        # 验证所有实例都成功响应
        successful_count = sum(
            1 for r in responses
            if not isinstance(r, BaseException) and r.status_code in [200, 405]
        )
        assert successful_count == len(multiple_server_instances), \
            f"所有 {len(multiple_server_instances)} 个实例都应响应，实际成功 {successful_count} 个"

    @pytest.mark.asyncio
    async def test_load_distribution_across_instances(
        self, multiple_server_instances: list[ServerInstance], http_client: httpx.AsyncClient
    ) -> None:
        """
        验证负载可以分布到多个实例。

        Given: 3 个服务器实例运行中
        When: 向每个实例发送多个并发请求
        Then: 每个实例都应该处理其请求
        """
        # 等待所有服务器启动
        for server in multiple_server_instances:
            await self._wait_for_server(server.base_url, http_client)

        # 向每个实例发送 5 个并发请求
        tasks = []
        for server in multiple_server_instances:
            for _ in range(5):
                tasks.append(http_client.head(f"{server.base_url}/mcp"))

        responses = await asyncio.gather(*tasks, return_exceptions=True)

        # 验证所有请求都成功
        successful_count = sum(
            1 for r in responses
            if not isinstance(r, BaseException) and r.status_code in [200, 405]
        )
        expected_count = len(multiple_server_instances) * 5
        assert successful_count == expected_count, \
            f"应成功处理 {expected_count} 个请求，实际成功 {successful_count} 个"

    async def _wait_for_server(
        self, base_url: str, client: httpx.AsyncClient, max_retries: int = 15
    ) -> None:
        """等待服务器启动完成。"""
        for _ in range(max_retries):
            try:
                response = await client.head(f"{base_url}/mcp", timeout=1.0)
                if response.status_code in [200, 405]:
                    return
            except (httpx.ConnectError, httpx.TimeoutException):
                time.sleep(0.5)
        raise AssertionError(f"服务器未能在 {max_retries * 0.5} 秒内启动")


class TestInstanceIndependence:
    """测试实例间独立性验证。"""

    @pytest.mark.asyncio
    async def test_two_instances_independent_startup(
        self, two_server_instances: list[ServerInstance]
    ) -> None:
        """
        验证 2 个服务器实例可以独立启动。

        Given: 启动 2 个服务器实例在不同端口
        When: 检查每个实例的进程状态
        Then: 两个实例都应该独立正常运行
        """
        for server in two_server_instances:
            assert server.is_alive(), \
                f"端口 {server.port} 的服务器实例未正常运行"

    @pytest.mark.asyncio
    async def test_concurrent_requests_to_each_instance(
        self, two_server_instances: list[ServerInstance], http_client: httpx.AsyncClient
    ) -> None:
        """
        验证向每个实例并发发送多个请求都能正常处理。

        Given: 2 个服务器实例运行中
        When: 向每个实例并发发送 10 个请求
        Then: 每个实例都应该正确处理所有请求
        """
        # 等待所有服务器启动
        for server in two_server_instances:
            await self._wait_for_server(server.base_url, http_client)

        # 向每个实例发送并发请求
        tasks_per_instance = 10
        all_tasks = []
        for server in two_server_instances:
            for _ in range(tasks_per_instance):
                all_tasks.append(http_client.head(f"{server.base_url}/mcp"))

        responses = await asyncio.gather(*all_tasks, return_exceptions=True)

        # 验证所有请求都成功
        successful_count = sum(
            1 for r in responses
            if not isinstance(r, BaseException) and r.status_code in [200, 405]
        )
        expected_count = len(two_server_instances) * tasks_per_instance
        assert successful_count == expected_count, \
            f"应成功处理 {expected_count} 个请求，实际成功 {successful_count} 个"

    @pytest.mark.asyncio
    async def test_instances_do_not_interfere(
        self, two_server_instances: list[ServerInstance], http_client: httpx.AsyncClient
    ) -> None:
        """
        验证实例之间互不干扰。

        Given: 2 个服务器实例运行中
        When: 并发向两个实例发送大量请求
        Then: 两个实例都应该独立处理请求，不会相互干扰
        """
        # 等待所有服务器启动
        for server in two_server_instances:
            await self._wait_for_server(server.base_url, http_client)

        # 向两个实例各发送 20 个并发请求
        num_requests = 20
        tasks = []
        for server in two_server_instances:
            for _ in range(num_requests):
                tasks.append(http_client.head(f"{server.base_url}/mcp"))

        # 执行所有请求
        responses = await asyncio.gather(*tasks, return_exceptions=True)

        # 验证所有请求都成功（无状态模式下实例独立处理）
        successful_count = sum(
            1 for r in responses
            if not isinstance(r, BaseException) and r.status_code in [200, 405]
        )
        expected_count = len(two_server_instances) * num_requests
        assert successful_count == expected_count, \
            f"两个实例应独立处理 {expected_count} 个请求，实际成功 {successful_count} 个"

    @pytest.mark.asyncio
    async def test_instance_failure_does_not_affect_others(
        self, two_server_instances: list[ServerInstance], http_client: httpx.AsyncClient
    ) -> None:
        """
        验证单个实例失败不影响其他实例。

        Given: 2 个服务器实例运行中
        When: 停止一个实例后向另一个实例发送请求
        Then: 另一个实例应该仍能正常响应
        """
        # 等待所有服务器启动
        for server in two_server_instances:
            await self._wait_for_server(server.base_url, http_client)

        # 停止第一个实例
        two_server_instances[0].stop()

        # 验证第二个实例仍能正常响应
        for _ in range(5):
            response = await http_client.head(f"{two_server_instances[1].base_url}/mcp")
            assert response.status_code in [200, 405], \
                "停止一个实例不应影响另一个实例的响应"

    async def _wait_for_server(
        self, base_url: str, client: httpx.AsyncClient, max_retries: int = 15
    ) -> None:
        """等待服务器启动完成。"""
        for _ in range(max_retries):
            try:
                response = await client.head(f"{base_url}/mcp", timeout=1.0)
                if response.status_code in [200, 405]:
                    return
            except (httpx.ConnectError, httpx.TimeoutException):
                time.sleep(0.5)
        raise AssertionError(f"服务器未能在 {max_retries * 0.5} 秒内启动")
