"""
负载测试器
模拟高并发访问场景
"""
import asyncio
import time
from typing import List, Dict, Any
from loguru import logger

from .types import LoadTestResult
from .runner import ConcurrentRunner


class LoadTester:
    """
    负载测试器
    模拟高并发访问场景
    """

    def __init__(self):
        self.results: List[Dict[str, Any]] = []

    async def run_load_test(
        self,
        url: str,
        concurrent_users: int = 10,
        total_requests: int = 100,
        timeout: float = 30,
    ) -> LoadTestResult:
        """
        运行负载测试

        Args:
            url: 目标URL
            concurrent_users: 并发用户数
            total_requests: 总请求数
            timeout: 超时时间

        Returns:
            LoadTestResult: 测试结果
        """
        from playwright.async_api import async_playwright

        result = LoadTestResult()
        durations = []
        start_time = time.time()

        async def make_request():
            request_start = time.time()

            try:
                async with async_playwright() as p:
                    browser = await p.chromium.launch(headless=True)
                    page = await browser.new_page()

                    await page.goto(url, timeout=timeout * 1000)

                    duration = (time.time() - request_start) * 1000
                    durations.append(duration)

                    await browser.close()

                    return {"success": True, "duration": duration}

            except Exception as e:
                return {"success": False, "error": str(e)}

        # 创建请求任务
        tasks = [make_request for _ in range(total_requests)]

        # 并发执行
        runner = ConcurrentRunner(max_workers=concurrent_users)
        results = await runner.run_with_limit(tasks, concurrency_limit=concurrent_users)

        # 统计结果
        total_time = (time.time() - start_time) * 1000

        for r in results:
            result.total_requests += 1
            if isinstance(r, dict):
                if r.get("success"):
                    result.successful_requests += 1
                else:
                    result.failed_requests += 1
                    if r.get("error"):
                        result.errors.append(r["error"])

        if durations:
            result.total_duration_ms = total_time
            result.avg_duration_ms = sum(durations) / len(durations)
            result.min_duration_ms = min(durations)
            result.max_duration_ms = max(durations)
            result.requests_per_second = result.total_requests / (total_time / 1000)

        logger.info(f"负载测试完成: {result.total_requests} 请求, {result.requests_per_second:.2f} req/s")

        return result

    async def run_stress_test(
        self,
        url: str,
        initial_users: int = 1,
        max_users: int = 50,
        step: int = 5,
        duration_per_step: int = 10,
    ) -> List[LoadTestResult]:
        """
        压力测试

        Args:
            url: 目标URL
            initial_users: 初始用户数
            max_users: 最大用户数
            step: 递增步长
            duration_per_step: 每步持续时间

        Returns:
            List[LoadTestResult]: 各阶段测试结果
        """
        results = []

        for users in range(initial_users, max_users + 1, step):
            logger.info(f"压力测试: {users} 并发用户")

            result = await self.run_load_test(
                url=url,
                concurrent_users=users,
                total_requests=users * 10,  # 每用户10个请求
            )

            result_dict = result.to_dict()
            result_dict["concurrent_users"] = users
            results.append(result)

            # 如果失败率超过50%，停止测试
            if result.failed_requests > result.total_requests * 0.5:
                logger.warning(f"失败率过高，停止压力测试: {users} 用户")
                break

            await asyncio.sleep(2)  # 阶段间隔

        return results

    async def run_spike_test(
        self,
        url: str,
        baseline_users: int = 5,
        spike_users: int = 50,
        baseline_duration: int = 30,
        spike_duration: int = 10,
    ) -> Dict[str, LoadTestResult]:
        """
        峰值测试

        Args:
            url: 目标URL
            baseline_users: 基线用户数
            spike_users: 峰值用户数
            baseline_duration: 基线持续时间
            spike_duration: 峰值持续时间

        Returns:
            Dict: 测试结果
        """
        results = {}

        # 基线测试
        logger.info(f"基线测试: {baseline_users} 用户")
        results["baseline"] = await self.run_load_test(
            url=url,
            concurrent_users=baseline_users,
            total_requests=baseline_users * baseline_duration,
        )

        # 峰值测试
        logger.info(f"峰值测试: {spike_users} 用户")
        results["spike"] = await self.run_load_test(
            url=url,
            concurrent_users=spike_users,
            total_requests=spike_users * spike_duration,
        )

        # 恢复测试
        logger.info(f"恢复测试: {baseline_users} 用户")
        results["recovery"] = await self.run_load_test(
            url=url,
            concurrent_users=baseline_users,
            total_requests=baseline_users * baseline_duration,
        )

        return results

    async def run_soak_test(
        self,
        url: str,
        concurrent_users: int = 10,
        duration_minutes: int = 5,
        requests_per_minute: int = 60,
    ) -> LoadTestResult:
        """
        浸泡测试（长时间稳定性测试）

        Args:
            url: 目标URL
            concurrent_users: 并发用户数
            duration_minutes: 测试时长（分钟）
            requests_per_minute: 每分钟请求数

        Returns:
            LoadTestResult: 测试结果
        """
        total_requests = duration_minutes * requests_per_minute

        logger.info(f"浸泡测试: {duration_minutes}分钟, {concurrent_users}并发用户")

        result = await self.run_load_test(
            url=url,
            concurrent_users=concurrent_users,
            total_requests=total_requests,
        )

        return result
