"""
数据库管理平台 API 调用客户端
用于调用数据库管理平台的所有 API 接口
"""

import httpx
import json
import os
from typing import Dict, Any, Optional

from .env_config import get_api_key, get_base_url
from .logger_config import get_logger

logger = get_logger(__name__)

# 默认超时配置（秒）
DEFAULT_TIMEOUT = 30.0


class AgileDBAPIClient:
    """数据库管理平台 API 客户端"""
    
    def __init__(
        self, 
        base_url: Optional[str] = None,
        api_key: Optional[str] = None,
        default_timeout: float = DEFAULT_TIMEOUT,
    ):
        """
        初始化 API 客户端
        
        Args:
            base_url: API 基础 URL（默认从环境变量 backendBaseUrl 读取）
            api_key: API 密钥（默认从环境变量 API_KEY 读取）
            default_timeout: 请求超时时间（秒），默认 30 秒
        """
        if base_url is None:
            base_url = get_base_url()
        
        if api_key is None:
            api_key = get_api_key()
        
        self.base_url = base_url.rstrip('/')
        self.api_key = api_key
        self.default_timeout = default_timeout
        self._client: Optional[httpx.AsyncClient] = None
        
        logger.info(f"[客户端初始化] base_url={self.base_url}")
    
    @property
    def client(self) -> httpx.AsyncClient:
        """懒加载 HTTP 客户端"""
        if self._client is None:
            self._client = httpx.AsyncClient(timeout=self.default_timeout)
        return self._client
        
    def _get_headers(self, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
        """获取请求头"""
        headers = {
            'Authorization': self.api_key if self.api_key.startswith('Bearer ') else f'Bearer {self.api_key}',
        }
        if extra_headers:
            headers.update(extra_headers)
        return headers
    
    def _build_url(self, path: str) -> str:
        """构建完整 URL

        约定：base_url 已包含完整地址（如含 /api 前缀则配在环境变量里），
        各工具传入的 path 不带 /api 前缀，此处直接拼接。
        """
        if path.startswith('http://') or path.startswith('https://'):
            return path
        return f"{self.base_url}{path}"
    
    def _handle_response(self, response: httpx.Response, url: str) -> Dict[str, Any]:
        """统一处理 API 响应"""
        logger.info(f"[API响应] HTTP {response.status_code}")

        if response.status_code == 204:
            return {"success": True, "data": None}

        # 先尝试解析 body，再判断状态码。
        # 平台会用非 2xx 状态码 + body 里的 {code, msg} 返回业务错误，
        # 若先 raise_for_status() 会在解析 body 前抛异常，导致真正的 msg 全部丢失。
        try:
            data = response.json()
        except (json.JSONDecodeError, UnicodeDecodeError):
            # 非 JSON 响应（如 Excel/文件下载，二进制内容 .json() 会抛 UnicodeDecodeError）
            response.raise_for_status()
            return {"success": True, "data": response.content, "raw": True}

        # 检查平台 API 的 {code, msg} 格式
        # 平台约定：code 为 200 或 0 均表示成功（见数据库模块文档 §1.1）
        if isinstance(data, dict) and 'code' in data:
            if data['code'] not in (0, 200):
                error_msg = data.get('msg', '未知错误')
                logger.error(f"[API错误] HTTP {response.status_code}, {error_msg}")
                raise Exception(error_msg)
            # code 合法即视为业务成功，不再用 HTTP 状态码二次否决
            return data

        # 无 {code} 信封的响应：回落到 HTTP 状态码判断，
        # 非 2xx 时把 body 文本带进异常，避免错误细节丢失。
        if response.status_code >= 400:
            detail = data if isinstance(data, str) else json.dumps(data, ensure_ascii=False)
            logger.error(f"[API错误] HTTP {response.status_code}, {detail}")
            raise Exception(f"HTTP {response.status_code}: {detail}")

        return data
    
    async def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """发送 GET 请求"""
        url = self._build_url(path)
        try:
            logger.info(f"[API请求] GET {url}")
            response = await self.client.get(url, headers=self._get_headers(), params=params)
            return self._handle_response(response, url)
        except httpx.TimeoutException:
            raise Exception(f"API 请求超时: {url}")
        except httpx.HTTPStatusError as e:
            raise Exception(f"API 请求失败 (HTTP {e.response.status_code}): {url}")
        except httpx.RequestError as e:
            raise Exception(f"API 请求异常: {url}, 错误: {str(e)}")

    async def post(self, path: str, json_data: Optional[Dict[str, Any]] = None, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """发送 POST 请求"""
        url = self._build_url(path)
        try:
            logger.info(f"[API请求] POST {url}")
            headers = self._get_headers({'Content-Type': 'application/json'})
            response = await self.client.post(url, headers=headers, json=json_data, params=params)
            return self._handle_response(response, url)
        except httpx.TimeoutException:
            raise Exception(f"API 请求超时: {url}")
        except httpx.HTTPStatusError as e:
            raise Exception(f"API 请求失败 (HTTP {e.response.status_code}): {url}")
        except httpx.RequestError as e:
            raise Exception(f"API 请求异常: {url}, 错误: {str(e)}")

    async def put(self, path: str, json_data: Optional[Dict[str, Any]] = None, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """发送 PUT 请求"""
        url = self._build_url(path)
        try:
            logger.info(f"[API请求] PUT {url}")
            headers = self._get_headers({'Content-Type': 'application/json'})
            response = await self.client.put(url, headers=headers, json=json_data, params=params)
            return self._handle_response(response, url)
        except httpx.TimeoutException:
            raise Exception(f"API 请求超时: {url}")
        except httpx.HTTPStatusError as e:
            raise Exception(f"API 请求失败 (HTTP {e.response.status_code}): {url}")
        except httpx.RequestError as e:
            raise Exception(f"API 请求异常: {url}, 错误: {str(e)}")

    async def delete(self, path: str, params: Optional[Dict[str, Any]] = None, json_data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """发送 DELETE 请求"""
        url = self._build_url(path)
        try:
            logger.info(f"[API请求] DELETE {url}")
            headers = self._get_headers()
            # 注意：httpx 的 client.delete() 便捷方法不接受 json 参数（仅 post/put/patch 有）。
            # 需要带 body 的 DELETE 必须走通用 request()，否则会抛 TypeError。
            if json_data is not None:
                headers['Content-Type'] = 'application/json'
                response = await self.client.request("DELETE", url, headers=headers, params=params, json=json_data)
            else:
                response = await self.client.delete(url, headers=headers, params=params)
            return self._handle_response(response, url)
        except httpx.TimeoutException:
            raise Exception(f"API 请求超时: {url}")
        except httpx.HTTPStatusError as e:
            raise Exception(f"API 请求失败 (HTTP {e.response.status_code}): {url}")
        except httpx.RequestError as e:
            raise Exception(f"API 请求异常: {url}, 错误: {str(e)}")

    async def upload(self, path: str, files: Dict[str, Any], params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """发送文件上传请求（multipart/form-data）"""
        url = self._build_url(path)
        try:
            logger.info(f"[API请求] UPLOAD {url}")
            # 文件上传不需要 Content-Type，httpx 会自动设置 multipart/form-data
            headers = self._get_headers()
            response = await self.client.post(url, headers=headers, files=files, params=params)
            return self._handle_response(response, url)
        except httpx.TimeoutException:
            raise Exception(f"API 请求超时: {url}")
        except httpx.HTTPStatusError as e:
            raise Exception(f"API 请求失败 (HTTP {e.response.status_code}): {url}")
        except httpx.RequestError as e:
            raise Exception(f"API 请求异常: {url}, 错误: {str(e)}")

    async def close(self):
        """关闭 HTTP 客户端"""
        if self._client is not None:
            await self._client.aclose()
            self._client = None
    
    async def __aenter__(self):
        return self
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.close()
        return False


# 懒加载的默认客户端
_default_client: Optional[AgileDBAPIClient] = None


def get_default_client() -> AgileDBAPIClient:
    """获取默认客户端（懒加载）"""
    global _default_client
    if _default_client is None:
        _default_client = AgileDBAPIClient()
    return _default_client
