"""批量关键词处理器.

支持异步批量处理、指数退避重试和进度报告。
"""

import asyncio
from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from src.keyword_research.google_planner import GoogleKeywordPlanner, KeywordData


@dataclass
class BatchResult:
    """批量处理结果.

    Attributes:
        keyword: 关键词
        data: 关键词数据
        success: 是否成功
        error: 错误信息（如果有）
    """

    keyword: str
    data: "KeywordData | None" = None
    success: bool = True
    error: str = ""


class BatchProcessor:
    """异步批量关键词处理器.

    支持并发处理、失败重试和进度回调。

    Example:
        >>> processor = BatchProcessor(planner)
        >>> results = await processor.process(["kw1", "kw2", "kw3"])
    """

    def __init__(
        self,
        planner: "GoogleKeywordPlanner",
        max_concurrent: int = 5,
        max_retries: int = 3,
    ) -> None:
        """初始化.

        Args:
            planner: GoogleKeywordPlanner 实例
            max_concurrent: 最大并发数
            max_retries: 最大重试次数
        """
        self.planner = planner
        self.max_concurrent = max_concurrent
        self.max_retries = max_retries

    async def process(
        self,
        keywords: list[str],
        progress_callback: Callable[[int, int], None] | None = None,
    ) -> list[BatchResult]:
        """批量处理关键词.

        Args:
            keywords: 关键词列表
            progress_callback: 进度回调函数 (completed, total)

        Returns:
            BatchResult 列表
        """
        semaphore = asyncio.Semaphore(self.max_concurrent)
        completed = 0
        total = len(keywords)

        async def process_one(keyword: str) -> BatchResult:
            nonlocal completed
            async with semaphore:
                for attempt in range(self.max_retries):
                    try:
                        data = await self.planner.get_keyword_data(keyword)
                        result = BatchResult(keyword=keyword, data=data, success=True)
                        break
                    except Exception as e:
                        if attempt == self.max_retries - 1:
                            result = BatchResult(keyword=keyword, success=False, error=str(e))
                        else:
                            await asyncio.sleep(2**attempt)  # 指数退避
                completed += 1
                if progress_callback:
                    progress_callback(completed, total)
                return result

        tasks = [process_one(kw) for kw in keywords]
        return await asyncio.gather(*tasks)

    async def process_with_progress(self, keywords: list[str]) -> dict[str, Any]:
        """处理并返回进度信息.

        Returns:
            包含结果、成功数、失败数的字典
        """
        results = await self.process(keywords)

        successful = [r for r in results if r.success]
        failed = [r for r in results if not r.success]

        return {
            "results": results,
            "successful_count": len(successful),
            "failed_count": len(failed),
            "total": len(keywords),
        }
