# -*- coding: utf-8 -*-
"""
V123Pan 主客户端模块
提供统一的 API 访问接口
"""

import asyncio
import logging
from typing import Optional, Dict, Any

import httpx

from .const import PLATFORM, CONTENT_TYPE, API_PATHS, BASE_URL, Api
from .api import Auth, User, Upload, FileManagement, FileQuery, Download
from .exceptions import (
    APIError,
    NetworkError,
    AuthError,
    RateLimitError,
)


class V123Pan(Auth, User, Upload, FileManagement, FileQuery, Download):
    """
    V123Pan 主客户端类

    继承所有 API 模块，提供统一的访问接口。

    Args:
        client_id: 开发者 ID（可选）
        client_secret: 开发者密钥（可选）
        access_token: 访问令牌（可选）

    Note:
        三者必须提供其一：
        - 直接提供 access_token（推荐用于生产环境）
        - 提供 client_id 和 client_secret（会自动获取 access_token）

    Example:
        >>> import asyncio
        >>> from v123pan import V123Pan
        >>>
        >>> async def main():
        ...     client = V123Pan(access_token="your_token")
        ...     try:
        ...         user_info = await client.user_info()
        ...         print(user_info)
        ...     finally:
        ...         await client.close()
        >>>
        >>> asyncio.run(main())
    """

    def __init__(
        self,
        client_id: Optional[str] = None,
        client_secret: Optional[str] = None,
        access_token: Optional[str] = None,
    ) -> None:
        """
        初始化 V123Pan 客户端

        Args:
            client_id: 开发者 ID
            client_secret: 开发者密钥
            access_token: 访问令牌

        Raises:
            ValueError: 当未提供任何认证信息时
        """
        self.client_id: Optional[str] = client_id
        self.client_secret: Optional[str] = client_secret
        self.access_token: Optional[str] = access_token
        self.access_valid: bool = bool(self.access_token)

        if not self.access_token and not (self.client_id and self.client_secret):
            raise ValueError(
                "必须提供 access_token 或 (client_id 和 client_secret) 之一"
            )

        self._init_client()
        self._init_logger()

    def _init_client(self) -> None:
        """初始化 HTTP 客户端"""
        self.client: httpx.AsyncClient = httpx.AsyncClient(
            limits=httpx.Limits(
                max_keepalive_connections=20,
                max_connections=100,
            ),
            timeout=httpx.Timeout(30.0),
            follow_redirects=True,
        )
        self.client.headers["Platform"] = PLATFORM
        self.client.headers["Content-Type"] = CONTENT_TYPE

    def _init_logger(self) -> None:
        """初始化日志记录器"""
        self.logger: logging.Logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)
        handler = logging.StreamHandler()
        formatter = logging.Formatter(
            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        )
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)

    def regist_logger(self, logger: logging.Logger) -> None:
        """
        注册自定义日志记录器

        Args:
            logger: 自定义日志记录器实例
        """
        self.logger = logger

    async def get_access_token(self) -> str:
        """
        获取访问令牌

        使用客户端 ID 和客户端密钥获取访问令牌。
        如果已提供 access_token，则直接返回。

        Returns:
            str: 访问令牌

        Raises:
            APIError: 获取令牌失败
        """
        if self.access_valid and self.access_token:
            return self.access_token

        if not self.client_id or not self.client_secret:
            raise APIError("无法获取 access_token：缺少 client_id 或 client_secret")

        result = await self.token(self.client_id, self.client_secret)
        return result["data"]["accessToken"]

    async def _send_request(
        self,
        api: Api,
        params: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        发送请求（带重试机制）

        Args:
            api: API 配置对象
            params: URL 参数
            json: JSON 请求体

        Returns:
            Dict[str, Any]: API 响应数据

        Raises:
            APIError: 请求失败
            NetworkError: 网络错误
            AuthError: 认证错误
            RateLimitError: QPS 限制
        """
        max_retries: int = 3
        base_delay: float = 1.0

        for retry_num in range(max_retries):
            if retry_num > 0:
                delay = base_delay * (2 ** (retry_num - 1))
                self.logger.info(f"第 {retry_num} 次重试，等待 {delay:.2f} 秒")
                await asyncio.sleep(delay)

            try:
                response = await self._request(api, params, json)
                return response
            except RateLimitError as e:
                if retry_num == max_retries - 1:
                    raise
                self.logger.warning(f"QPS 限制触发: {e}")
            except (NetworkError, httpx.RequestError) as e:
                if retry_num == max_retries - 1:
                    raise
                self.logger.warning(f"网络错误，将重试: {e}")
            except (AuthError, APIError, ValueError) as e:
                self.logger.error(f"错误: {e}")
                raise

        raise APIError("请求失败，已达到最大重试次数")

    async def _request(
        self,
        api: Api,
        params: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        发送 HTTP 请求

        Args:
            api: API 配置对象
            params: URL 参数
            json: JSON 请求体

        Returns:
            Dict[str, Any]: API 响应数据

        Raises:
            NetworkError: 网络错误
            APIError: API 错误
            AuthError: 认证错误
            RateLimitError: QPS 限制
        """
        self.logger.debug(f"Request: {api.method} {api.url}")

        headers: Dict[str, str] = {
            "Platform": PLATFORM,
            "Content-Type": CONTENT_TYPE,
        }

        if api.url != API_PATHS.access_token.url:
            access_token = await self.get_access_token()
            headers["Authorization"] = f"Bearer {access_token}"

        async with api.semaphore:
            try:
                response = await asyncio.wait_for(
                    self.client.request(
                        api.method,
                        str(api.url),
                        params=params,
                        json=json,
                        headers=headers,
                    ),
                    timeout=30.0,
                )
            except asyncio.TimeoutError:
                raise NetworkError("请求超时")
            except httpx.RequestError as e:
                raise NetworkError(f"网络错误: {e}")

        self.logger.debug(f"Response: {response.status_code} {response.text}")

        try:
            result = response.json()
        except Exception as e:
            raise APIError(f"JSON 解析失败: {response.text}")

        if result is None:
            raise APIError("响应为空")

        if result.get("code") != 0:
            code = result.get("code")
            message = result.get("message", "未知错误")

            if code == 401:
                raise AuthError(message, code=code, data=result)
            elif code == 429:
                raise RateLimitError(message, code=code, data=result)
            else:
                raise APIError(message, code=code, data=result)

        return result

    async def close(self) -> None:
        """
        关闭客户端，释放资源

        应该在使用完毕后调用此方法，特别是在异步上下文中。

        Example:
            >>> client = V123Pan(client_id="id", client_secret="secret")
            >>> # 使用客户端...
            >>> await client.close()
        """
        if hasattr(self, "client") and self.client is not None:
            await self.client.aclose()
            self.client = None

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

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