"""同步 HTTP 客户端实现"""

import httpx
import threading
from typing import Optional, Dict, Any, Union
from .retry import RetryHandler
from .exceptions import (
    OctenAPIError,
    OctenTimeoutError,
    OctenConnectionError,
    OctenRateLimitError,
    OctenAuthenticationError,
)
from ..utils.constants import (
    DEFAULT_TIMEOUT,
    DEFAULT_MAX_RETRIES,
    DEFAULT_MAX_CONNECTIONS,
    DEFAULT_MAX_KEEPALIVE_CONNECTIONS,
    DEFAULT_KEEPALIVE_EXPIRY,
    HEADER_API_KEY,
    HEADER_CONTENT_TYPE,
    HEADER_USER_AGENT,
    USER_AGENT_TEMPLATE,
)


class HTTPClient:
    """同步 HTTP 客户端

    支持连接池、长连接、HTTP/2 和自动重试。
    使用线程本地存储确保线程安全。

    Attributes:
        base_url: API 基础 URL
        api_key: API 密钥
        timeout: 默认超时时间（秒）
        max_retries: 最大重试次数
        http2: 是否启用 HTTP/2
    """

    def __init__(
        self,
        base_url: str,
        api_key: str,
        timeout: float = DEFAULT_TIMEOUT,
        max_retries: int = DEFAULT_MAX_RETRIES,
        http2: bool = True,
        max_connections: int = DEFAULT_MAX_CONNECTIONS,
        max_keepalive_connections: int = DEFAULT_MAX_KEEPALIVE_CONNECTIONS,
        keepalive_expiry: float = DEFAULT_KEEPALIVE_EXPIRY,
    ):
        self.base_url = base_url
        self.api_key = api_key
        self.timeout = timeout
        self.max_retries = max_retries
        self.http2 = http2
        self.max_connections = max_connections
        self.max_keepalive_connections = max_keepalive_connections
        self.keepalive_expiry = keepalive_expiry

        # 获取版本号
        try:
            from ..version import __version__
            self.user_agent = USER_AGENT_TEMPLATE.format(version=__version__)
        except ImportError:
            self.user_agent = USER_AGENT_TEMPLATE.format(version="unknown")

        # 使用线程本地存储，确保每个线程有独立的客户端实例
        self._local = threading.local()

        # 记录所有创建的客户端，用于清理
        self._clients_lock = threading.Lock()
        self._all_clients = []

        # 初始化重试处理器
        self._retry_handler = RetryHandler(max_retries=max_retries)

    def _get_client(self) -> httpx.Client:
        """获取当前线程的 httpx.Client 实例

        如果当前线程还没有客户端，则创建一个新的。
        这确保了每个线程使用独立的客户端实例，避免线程安全问题。

        Returns:
            当前线程的 httpx.Client 实例
        """
        if not hasattr(self._local, "client") or self._local.client is None:
            # 配置连接池
            limits = httpx.Limits(
                max_connections=self.max_connections,
                max_keepalive_connections=self.max_keepalive_connections,
                keepalive_expiry=self.keepalive_expiry,
            )

            # 构建请求头
            headers = {
                HEADER_API_KEY: self.api_key,
                HEADER_CONTENT_TYPE: "application/json",
                HEADER_USER_AGENT: self.user_agent,
            }

            # 为当前线程创建新的客户端
            self._local.client = httpx.Client(
                base_url=self.base_url,
                timeout=httpx.Timeout(self.timeout),
                http2=self.http2,
                limits=limits,
                headers=headers,
                follow_redirects=True,
            )

            # 记录客户端以便后续清理
            with self._clients_lock:
                self._all_clients.append(self._local.client)

        return self._local.client

    def request(
        self,
        method: str,
        endpoint: str,
        timeout: Optional[float] = None,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
        **kwargs,
    ) -> Dict[str, Any]:
        """发送 HTTP 请求

        Args:
            method: HTTP 方法（GET, POST 等）
            endpoint: API 端点路径
            timeout: 请求超时时间（秒），覆盖默认值
            json: JSON 请求体
            params: URL 查询参数
            **kwargs: 其他传递给 httpx 的参数

        Returns:
            响应的 JSON 数据

        Raises:
            OctenAPIError: API 返回错误
            OctenTimeoutError: 请求超时
            OctenConnectionError: 连接错误
        """

        def _make_request() -> Dict[str, Any]:
            try:
                # 使用自定义超时或默认超时
                request_timeout = timeout if timeout is not None else self.timeout

                # 获取当前线程的客户端实例
                client = self._get_client()

                response = client.request(
                    method=method,
                    url=endpoint,
                    json=json,
                    params=params,
                    timeout=request_timeout,
                    **kwargs,
                )

                # 处理响应
                return self._handle_response(response)

            except httpx.TimeoutException as e:
                raise OctenTimeoutError(
                    message=f"Request timed out: {str(e)}",
                    timeout=timeout or self.timeout,
                )
            except httpx.ConnectError as e:
                raise OctenConnectionError(
                    message=f"Connection failed: {str(e)}",
                )
            except httpx.HTTPError as e:
                raise OctenConnectionError(
                    message=f"HTTP error occurred: {str(e)}",
                )

        # 使用重试处理器执行请求
        return self._retry_handler.execute(_make_request)

    def _handle_response(self, response: httpx.Response) -> Dict[str, Any]:
        """处理 HTTP 响应

        Args:
            response: httpx 响应对象

        Returns:
            响应的 JSON 数据

        Raises:
            OctenAPIError: API 返回错误
            OctenRateLimitError: 速率限制错误
            OctenAuthenticationError: 认证错误
        """
        # 提取请求 ID（如果存在）
        request_id = response.headers.get("x-request-id")

        # 尝试解析 JSON 一次（性能优化）
        response_data = self._safe_json(response)

        # 处理成功响应（2xx）
        if response.is_success:
            if response_data is not None:
                return response_data
            else:
                raise OctenAPIError(
                    message="Failed to parse response JSON",
                    status_code=response.status_code,
                    request_id=request_id,
                )

        # 处理错误响应 - 从已解析的 JSON 中提取错误消息
        error_message = self._extract_error_message_from_data(response_data, response)

        # 认证错误（401）
        if response.status_code == 401:
            raise OctenAuthenticationError(
                message=error_message,
                response_data=response_data,
                request_id=request_id,
            )

        # 速率限制错误（429）
        if response.status_code == 429:
            retry_after = response.headers.get("retry-after")
            retry_after_seconds = int(retry_after) if retry_after else None
            raise OctenRateLimitError(
                message=error_message,
                retry_after=retry_after_seconds,
                response_data=response_data,
                request_id=request_id,
            )

        # 其他 API 错误
        raise OctenAPIError(
            message=error_message,
            status_code=response.status_code,
            response_data=response_data,
            request_id=request_id,
        )

    def _extract_error_message_from_data(
        self, data: Optional[Dict[str, Any]], response: httpx.Response
    ) -> str:
        """从已解析的 JSON 数据中提取错误消息

        Args:
            data: 已解析的 JSON 数据
            response: httpx 响应对象

        Returns:
            错误消息字符串
        """
        # 尝试从响应中提取错误消息
        if isinstance(data, dict):
            if "msg" in data:
                return data["msg"]
            if "message" in data:
                return data["message"]
            if "error" in data:
                return str(data["error"])

        # 使用默认错误消息
        return f"HTTP {response.status_code}: {response.reason_phrase}"

    def _safe_json(self, response: httpx.Response) -> Optional[Dict[str, Any]]:
        """安全地解析 JSON 响应

        Args:
            response: httpx 响应对象

        Returns:
            JSON 数据或 None
        """
        try:
            return response.json()
        except Exception:
            return None

    def close(self):
        """关闭所有线程的 HTTP 客户端，释放连接池资源"""
        with self._clients_lock:
            for client in self._all_clients:
                try:
                    client.close()
                except Exception:
                    pass  # 忽略关闭时的错误
            self._all_clients.clear()

        # 清理当前线程的客户端引用
        if hasattr(self._local, "client"):
            self._local.client = None

    def __enter__(self):
        """支持上下文管理器"""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """退出上下文管理器时自动关闭"""
        self.close()
