# -*- coding: utf-8 -*-
"""
批量文件处理模块
提供批量复制、移动、重命名、删除等操作
"""

import asyncio
from typing import List, Dict, Any, Optional, Tuple, Callable, AsyncIterator
from dataclasses import dataclass, field

from ...const import API_PATHS, BATCH_LIMITS
from ..base import BaseMixin


@dataclass
class BatchResult:
    """批量操作结果"""
    total: int = 0                    # 总数量
    success_count: int = 0            # 成功数量
    failed_count: int = 0             # 失败数量
    success_items: List[Any] = field(default_factory=list)  # 成功项
    failed_items: List[Dict] = field(default_factory=list)  # 失败项（包含原因）

    @property
    def success_rate(self) -> float:
        """成功率"""
        if self.total == 0:
            return 0.0
        return (self.success_count / self.total) * 100

    def __repr__(self) -> str:
        return (f"BatchResult(total={self.total}, success={self.success_count}, "
                f"failed={self.failed_count}, rate={self.success_rate:.1f}%)")


@dataclass
class CopyTaskResult:
    """单个复制任务结果"""
    task_id: int
    status: int  # 0-待处理，1-进行中，2-已完成，3-失败
    file_ids: List[int]


@dataclass
class BatchProgress:
    """批量操作进度"""
    current_step: int       # 当前步骤
    total_steps: int        # 总步骤数
    processed_count: int    # 已处理数量
    total_count: int        # 总数量
    message: str = ""       # 状态消息

    @property
    def percentage(self) -> float:
        """进度百分比"""
        if self.total_steps == 0:
            return 0.0
        return (self.current_step / self.total_steps) * 100

    def __repr__(self) -> str:
        return (f"BatchProgress(step={self.current_step}/{self.total_steps}, "
                f"processed={self.processed_count}/{self.total_count}, "
                f"progress={self.percentage:.1f}%)")


ProgressCallback = Callable[[BatchProgress], None]


class FileBatchOperator(BaseMixin):
    """
    批量文件操作器

    提供批量复制、移动、重命名、删除等操作，通过组合多个单步 API 实现。

    特性:
    - 自动分批处理大量文件
    - 支持进度回调
    - 完整的错误处理
    - 返回详细的成功/失败统计

    使用示例:
        >>> batch = FileBatchOperator(client)
        >>> result = await batch.copy_batch([1, 2, 3], target_dir_id=100)
        >>> print(f"复制成功：{result.success_count}/{result.total}")
    """

    def __init__(self, client):
        """
        初始化批量操作器

        Args:
            client: V123Pan 客户端实例
        """
        self.client = client
        self._move_batch_size = BATCH_LIMITS.MOVE_BATCH_SIZE
        self._delete_batch_size = BATCH_LIMITS.DELETE_BATCH_SIZE
        self._rename_batch_size = BATCH_LIMITS.RENAME_BATCH_SIZE
        self._copy_async_batch_size = BATCH_LIMITS.COPY_ASYNC_BATCH_SIZE

    async def copy_batch(
        self,
        file_ids: List[int],
        target_dir_id: int,
        progress_callback: Optional[ProgressCallback] = None
    ) -> BatchResult:
        """
        批量复制文件

        使用异步复制接口批量复制文件，单级最多支持 3000 个文件。

        Args:
            file_ids: 要复制的文件 ID 列表
            target_dir_id: 目标目录 ID
            progress_callback: 进度回调函数（可选）

        Returns:
            BatchResult: 批量操作结果

        Example:
            >>> result = await batch.copy_batch([12345, 12346], target_dir_id=67890)
            >>> print(f"复制成功：{result.success_count}/{result.total}")
        """
        result = BatchResult(total=len(file_ids))

        if not file_ids:
            return result

        # 分批处理
        batches = self._split_list(file_ids, self._copy_async_batch_size)
        total_batches = len(batches)

        self.client.logger.info(f"开始批量复制 {len(file_ids)} 个文件，分为 {total_batches} 批")

        task_ids: List[int] = []
        batch_file_ids: Dict[int, List[int]] = {}  # task_id -> file_ids

        # 提交所有复制任务
        for i, batch in enumerate(batches):
            try:
                copy_result = await self.client.copy_async(batch, target_dir_id)
                task_id = copy_result["data"]["taskId"]
                task_ids.append(task_id)
                batch_file_ids[task_id] = batch

                if progress_callback:
                    progress = BatchProgress(
                        current_step=i + 1,
                        total_steps=total_batches * 2,  # 提交 + 轮询
                        processed_count=len(batch),
                        total_count=len(file_ids),
                        message=f"提交复制任务 {i + 1}/{total_batches}"
                    )
                    progress_callback(progress)

            except Exception as e:
                self.client.logger.error(f"提交复制任务失败：{e}")
                result.failed_count += len(batch)
                result.failed_items.append({
                    "file_ids": batch,
                    "error": str(e),
                    "stage": "submit"
                })

        # 轮询所有任务
        self.client.logger.info(f"等待 {len(task_ids)} 个复制任务完成")

        for i, task_id in enumerate(task_ids):
            try:
                final_status = await self._wait_copy_task(task_id)

                if final_status == 2:  # 已完成
                    completed_files = batch_file_ids[task_id]
                    result.success_count += len(completed_files)
                    result.success_items.extend(completed_files)
                else:
                    failed_files = batch_file_ids[task_id]
                    result.failed_count += len(failed_files)
                    result.failed_items.append({
                        "file_ids": failed_files,
                        "task_id": task_id,
                        "error": "复制任务失败",
                        "stage": "poll"
                    })

            except Exception as e:
                self.client.logger.error(f"轮询复制任务失败：{e}")
                failed_files = batch_file_ids[task_id]
                result.failed_count += len(failed_files)
                result.failed_items.append({
                    "file_ids": failed_files,
                    "task_id": task_id,
                    "error": str(e),
                    "stage": "poll"
                })

            if progress_callback:
                progress = BatchProgress(
                    current_step=total_batches + i + 1,
                    total_steps=total_batches * 2,
                    processed_count=result.success_count + result.failed_count,
                    total_count=len(file_ids),
                    message=f"轮询进度 {i + 1}/{len(task_ids)}"
                )
                progress_callback(progress)

        self.client.logger.info(f"批量复制完成：{result.success_count}成功，{result.failed_count}失败")
        return result

    async def _wait_copy_task(self, task_id: int, max_polls: int = 120, poll_interval: float = 1.0) -> int:
        """
        轮询复制任务直到完成

        Args:
            task_id: 任务 ID
            max_polls: 最大轮询次数
            poll_interval: 轮询间隔（秒）

        Returns:
            int: 最终任务状态（0-待处理，1-进行中，2-已完成，3-失败）
        """
        for _ in range(max_polls):
            result = await self.client.copy_progress(task_id)
            status = result["data"]["status"]

            if status in (2, 3):  # 已完成或失败
                return status

            await asyncio.sleep(poll_interval)

        raise TimeoutError(f"复制任务超时：task_id={task_id}")

    async def move_batch(
        self,
        file_ids: List[int],
        target_dir_id: int,
        progress_callback: Optional[ProgressCallback] = None
    ) -> BatchResult:
        """
        批量移动文件

        分批调用 move 接口移动文件，每批最多 100 个文件。

        Args:
            file_ids: 要移动的文件 ID 列表
            target_dir_id: 目标目录 ID
            progress_callback: 进度回调函数（可选）

        Returns:
            BatchResult: 批量操作结果

        Example:
            >>> result = await batch.move_batch([12345, 12346], target_dir_id=67890)
            >>> print(f"移动成功：{result.success_count}/{result.total}")
        """
        result = BatchResult(total=len(file_ids))

        if not file_ids:
            return result

        batches = self._split_list(file_ids, self._move_batch_size)
        total_batches = len(batches)

        self.client.logger.info(f"开始批量移动 {len(file_ids)} 个文件，分为 {total_batches} 批")

        for i, batch in enumerate(batches):
            try:
                await self.client.move(batch, target_dir_id)
                result.success_count += len(batch)
                result.success_items.extend(batch)

            except Exception as e:
                self.client.logger.error(f"移动文件失败：{e}")
                result.failed_count += len(batch)
                result.failed_items.append({
                    "file_ids": batch,
                    "error": str(e),
                    "target_dir_id": target_dir_id
                })

            if progress_callback:
                progress = BatchProgress(
                    current_step=i + 1,
                    total_steps=total_batches,
                    processed_count=result.success_count + result.failed_count,
                    total_count=len(file_ids),
                    message=f"移动进度 {i + 1}/{total_batches}"
                )
                progress_callback(progress)

        self.client.logger.info(f"批量移动完成：{result.success_count}成功，{result.failed_count}失败")
        return result

    async def rename_batch_extended(
        self,
        rename_list: List[Tuple[int, str]],
        progress_callback: Optional[ProgressCallback] = None
    ) -> BatchResult:
        """
        批量重命名文件（扩展版）

        分批调用 rename_batch 接口重命名文件，每批最多 30 个文件。

        Args:
            rename_list: 重命名列表，每个元素为 (文件 ID, 新文件名) 元组
            progress_callback: 进度回调函数（可选）

        Returns:
            BatchResult: 批量操作结果

        Example:
            >>> result = await batch.rename_batch_extended([(12345, "新文件名.txt")])
            >>> print(f"重命名成功：{result.success_count}/{result.total}")
        """
        result = BatchResult(total=len(rename_list))

        if not rename_list:
            return result

        # 转换为 API 格式 "文件 ID|文件名"
        rename_strings = [f"{file_id}|{filename}" for file_id, filename in rename_list]

        batches = self._split_list(rename_strings, self._rename_batch_size)
        total_batches = len(batches)

        self.client.logger.info(f"开始批量重命名 {len(rename_list)} 个文件，分为 {total_batches} 批")

        for i, batch in enumerate(batches):
            try:
                rename_result = await self.client.rename_batch(batch)

                # 处理成功列表
                if "successList" in rename_result.get("data", {}):
                    for item in rename_result["data"]["successList"]:
                        file_id = item.get("fileID")
                        result.success_count += 1
                        result.success_items.append(file_id)

                # 处理失败列表
                if "failList" in rename_result.get("data", {}):
                    for item in rename_result["data"]["failList"]:
                        file_id = item.get("fileID")
                        message = item.get("message", "未知错误")
                        result.failed_count += 1
                        result.failed_items.append({
                            "file_id": file_id,
                            "error": message
                        })

            except Exception as e:
                self.client.logger.error(f"重命名文件失败：{e}")
                # 无法确定哪些失败，整批标记为失败
                result.failed_count += len(batch)
                result.failed_items.append({
                    "batch": batch,
                    "error": str(e)
                })

            if progress_callback:
                progress = BatchProgress(
                    current_step=i + 1,
                    total_steps=total_batches,
                    processed_count=result.success_count + result.failed_count,
                    total_count=len(rename_list),
                    message=f"重命名进度 {i + 1}/{total_batches}"
                )
                progress_callback(progress)

        self.client.logger.info(f"批量重命名完成：{result.success_count}成功，{result.failed_count}失败")
        return result

    async def delete_batch(
        self,
        file_ids: List[int],
        progress_callback: Optional[ProgressCallback] = None
    ) -> BatchResult:
        """
        批量删除文件

        分批调用 delete 接口删除文件，每批最多 100 个文件。

        Args:
            file_ids: 要删除的文件 ID 列表
            progress_callback: 进度回调函数（可选）

        Returns:
            BatchResult: 批量操作结果

        Example:
            >>> result = await batch.delete_batch([12345, 12346])
            >>> print(f"删除成功：{result.success_count}/{result.total}")
        """
        result = BatchResult(total=len(file_ids))

        if not file_ids:
            return result

        batches = self._split_list(file_ids, self._delete_batch_size)
        total_batches = len(batches)

        self.client.logger.info(f"开始批量删除 {len(file_ids)} 个文件，分为 {total_batches} 批")

        for i, batch in enumerate(batches):
            try:
                await self.client.delete(batch)
                result.success_count += len(batch)
                result.success_items.extend(batch)

            except Exception as e:
                self.client.logger.error(f"删除文件失败：{e}")
                result.failed_count += len(batch)
                result.failed_items.append({
                    "file_ids": batch,
                    "error": str(e)
                })

            if progress_callback:
                progress = BatchProgress(
                    current_step=i + 1,
                    total_steps=total_batches,
                    processed_count=result.success_count + result.failed_count,
                    total_count=len(file_ids),
                    message=f"删除进度 {i + 1}/{total_batches}"
                )
                progress_callback(progress)

        self.client.logger.info(f"批量删除完成：{result.success_count}成功，{result.failed_count}失败")
        return result

    async def list_files_batch(
        self,
        parent_dir_id: int,
        page_size: int = 100,
        max_pages: Optional[int] = None,
        progress_callback: Optional[ProgressCallback] = None
    ) -> List[Dict[str, Any]]:
        """
        批量获取文件列表

        自动分页获取指定目录下的所有文件列表。

        Args:
            parent_dir_id: 父目录 ID
            page_size: 每页大小，默认 100
            max_pages: 最大页数，None 表示获取所有页
            progress_callback: 进度回调函数（可选）

        Returns:
            List[Dict[str, Any]]: 文件列表

        Example:
            >>> files = await batch.list_files_batch(0)
            >>> print(f"共获取 {len(files)} 个文件")
        """
        all_files: List[Dict[str, Any]] = []
        page = 1

        while True:
            try:
                list_result = await self.client.list(parent_dir_id, page_size, page)

                files = list_result.get("data", {}).get("list", [])
                if not files:
                    break

                all_files.extend(files)

                if progress_callback:
                    progress = BatchProgress(
                        current_step=page,
                        total_steps=max_pages or page,
                        processed_count=len(all_files),
                        total_count=0,  # 未知总数
                        message=f"获取第 {page} 页，共 {len(all_files)} 个文件"
                    )
                    progress_callback(progress)

                # 检查是否还有更多页
                total = list_result.get("data", {}).get("total", 0)
                if len(all_files) >= total:
                    break

                if max_pages and page >= max_pages:
                    break

                page += 1

            except Exception as e:
                self.client.logger.error(f"获取文件列表失败：{e}")
                break

        self.client.logger.info(f"批量获取文件列表完成：共 {len(all_files)} 个文件")
        return all_files

    def _split_list(self, items: List[Any], batch_size: int) -> List[List[Any]]:
        """
        将列表分割为多个批次

        Args:
            items: 要分割的列表
            batch_size: 每批大小

        Returns:
            List[List[Any]]: 分割后的批次列表
        """
        return [items[i:i + batch_size] for i in range(0, len(items), batch_size)]

    async def __aenter__(self) -> 'FileBatchOperator':
        """异步上下文管理器入口"""
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb) -> None:
        """异步上下文管理器退出"""
        pass

    async def get_files_info_batch(
        self,
        file_ids: List[int],
        batch_size: int = BATCH_LIMITS.FILE_INFO_BATCH_SIZE
    ) -> AsyncIterator[Dict[str, Any]]:
        """
        批量获取文件信息（异步生成器）

        分批调用 infos() 接口获取文件的详细信息，使用异步生成器逐条返回结果。

        Args:
            file_ids: 要获取信息的文件 ID 列表
            batch_size: 每批文件数量，默认 100

        Yields:
            Dict[str, Any]: 文件信息字典

        Example:
            >>> async for file_info in batch.get_files_info_batch([12345, 12346]):
            ...     print(f"{file_info['filename']}: {file_info['size']} 字节")
        """
        if not file_ids:
            return

        # 分批处理
        batches = self._split_list(file_ids, batch_size)

        for batch in batches:
            info_result = await self.client.infos(batch)
            file_list = info_result.get("data", {}).get("fileList", [])
            for file_info in file_list:
                yield file_info
