"""
并发执行器
支持并发执行多个任务
"""
import asyncio
from typing import Optional, List, Any, Callable


class ConcurrentRunner:
    """
    并发执行器
    支持并发执行多个浏览器任务
    """

    def __init__(self, max_workers: int = 5):
        self.max_workers = max_workers

    async def run_parallel(
        self,
        tasks: List[Callable],
        timeout: float = 60,
    ) -> List[Any]:
        """
        并行执行多个任务

        Args:
            tasks: 任务列表
            timeout: 超时时间

        Returns:
            List: 结果列表
        """
        results = []

        async def run_task(task: Callable):
            try:
                if asyncio.iscoroutinefunction(task):
                    return await asyncio.wait_for(task(), timeout=timeout)
                else:
                    return task()
            except asyncio.TimeoutError:
                return {"error": "timeout"}
            except Exception as e:
                return {"error": str(e)}

        # 创建任务
        coroutines = [run_task(task) for task in tasks]

        # 并行执行
        results = await asyncio.gather(*coroutines, return_exceptions=True)

        return results

    async def run_with_limit(
        self,
        tasks: List[Callable],
        concurrency_limit: int = None,
        timeout: float = 60,
    ) -> List[Any]:
        """
        限制并发数执行

        Args:
            tasks: 任务列表
            concurrency_limit: 并发限制
            timeout: 超时时间

        Returns:
            List: 结果列表
        """
        limit = concurrency_limit or self.max_workers
        semaphore = asyncio.Semaphore(limit)

        results = [None] * len(tasks)

        async def run_with_semaphore(index: int, task: Callable):
            async with semaphore:
                try:
                    if asyncio.iscoroutinefunction(task):
                        results[index] = await asyncio.wait_for(task(), timeout=timeout)
                    else:
                        results[index] = task()
                except Exception as e:
                    results[index] = {"error": str(e)}

        # 创建所有任务
        await asyncio.gather(*[
            run_with_semaphore(i, task) for i, task in enumerate(tasks)
        ])

        return results

    async def run_batch(
        self,
        tasks: List[Callable],
        batch_size: int = 5,
        timeout: float = 60,
    ) -> List[Any]:
        """
        分批执行任务

        Args:
            tasks: 任务列表
            batch_size: 每批数量
            timeout: 超时时间

        Returns:
            List: 结果列表
        """
        all_results = []

        for i in range(0, len(tasks), batch_size):
            batch = tasks[i:i + batch_size]
            results = await self.run_parallel(batch, timeout=timeout)
            all_results.extend(results)

        return all_results

    async def run_with_retry(
        self,
        tasks: List[Callable],
        max_retries: int = 3,
        timeout: float = 60,
    ) -> List[Any]:
        """
        带重试的并发执行

        Args:
            tasks: 任务列表
            max_retries: 最大重试次数
            timeout: 超时时间

        Returns:
            List: 结果列表
        """
        results = [None] * len(tasks)
        pending = list(enumerate(tasks))

        for attempt in range(max_retries + 1):
            if not pending:
                break

            current_tasks = [(i, t) for i, t in pending]
            pending = []

            for idx, task in current_tasks:
                try:
                    if asyncio.iscoroutinefunction(task):
                        results[idx] = await asyncio.wait_for(task(), timeout=timeout)
                    else:
                        results[idx] = task()
                except Exception as e:
                    if attempt < max_retries:
                        pending.append((idx, task))
                    else:
                        results[idx] = {"error": str(e)}

        return results
