"""
文心一言 (百度千帆) 平台适配器 - 使用百度千帆 API 获取真实AI回答

API文档: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/flfmc9do2
使用 ERNIE-Bot (completions) chat completion API
认证方式: API Key + Secret Key 换取 access_token
"""

import asyncio
import logging
import time
from typing import Optional

import httpx

from app.config import settings
from app.workers.platforms.base import BasePlatformAdapter
from app.workers.platforms.search_engine import fetch_search_content

logger = logging.getLogger(__name__)

# 模块级频率限制器
_last_request_time: float = 0.0
_min_interval: float = 6.0  # 每分钟10次 -> 6秒间隔

# access_token 缓存
_cached_token: Optional[str] = None
_token_expires_at: float = 0.0


async def _rate_limit_wait():
    """确保请求间隔不低于最小间隔"""
    global _last_request_time
    now = time.monotonic()
    elapsed = now - _last_request_time
    if elapsed < _min_interval:
        wait_time = _min_interval - elapsed
        logger.debug(f"文心一言 频率限制等待 {wait_time:.1f}s")
        await asyncio.sleep(wait_time)
    _last_request_time = time.monotonic()


class WenxinAdapter(BasePlatformAdapter):
    """文心一言平台适配器 - 使用百度千帆 API 获取真实AI回答"""

    platform_name = "wenxin"
    platform_url = "https://yiyan.baidu.com"

    # 百度千帆 API 端点
    _token_url = "https://aip.baidubce.com/oauth/2.0/token"
    # ERNIE-Bot 4.0 (completions_pro) 或 ERNIE-Bot (completions)
    _chat_url_template = (
        "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/{model}"
        "?access_token={token}"
    )
    _default_model = "completions_pro"  # ERNIE-Bot 4.0

    def __init__(self):
        self._api_key: Optional[str] = None
        self._secret_key: Optional[str] = None
        self._client: Optional[httpx.AsyncClient] = None

    @property
    def api_key(self) -> Optional[str]:
        if self._api_key is None:
            self._api_key = settings.BAIDU_QIANFAN_API_KEY
        return self._api_key

    @property
    def secret_key(self) -> Optional[str]:
        if self._secret_key is None:
            self._secret_key = settings.BAIDU_QIANFAN_SECRET_KEY
        return self._secret_key

    @property
    def is_configured(self) -> bool:
        return bool(
            self.api_key and self.api_key.strip()
            and self.secret_key and self.secret_key.strip()
        )

    def _get_client(self) -> httpx.AsyncClient:
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(
                timeout=httpx.Timeout(60.0, connect=10.0),
            )
        return self._client

    async def _get_access_token(self) -> str:
        """获取百度千帆 access_token，带缓存"""
        global _cached_token, _token_expires_at

        now = time.monotonic()
        if _cached_token and now < _token_expires_at:
            return _cached_token

        client = self._get_client()
        response = await client.post(
            self._token_url,
            params={
                "grant_type": "client_credentials",
                "client_id": self.api_key,
                "client_secret": self.secret_key,
            },
        )

        if response.status_code != 200:
            raise RuntimeError(
                f"百度千帆获取 access_token 失败: {response.status_code} {response.text[:300]}"
            )

        data = response.json()
        token = data.get("access_token")
        if not token:
            error_desc = data.get("error_description", "未知错误")
            raise RuntimeError(f"百度千帆获取 access_token 失败: {error_desc}")

        # 缓存 token，提前5分钟过期
        expires_in = data.get("expires_in", 2592000)  # 默认30天
        _cached_token = token
        _token_expires_at = now + expires_in - 300

        logger.info("百度千帆 access_token 获取成功")
        return token

    async def query(self, keyword: str) -> str:
        """在文心一言查询关键词，返回原始响应文本"""
        last_error = None
        for attempt in range(3):
            try:
                return await self._do_query(keyword)
            except Exception as e:
                last_error = e
                logger.warning(f"文心一言查询第 {attempt + 1} 次尝试失败: {e}")
                if attempt < 2:
                    await asyncio.sleep(2 ** attempt)

        # 所有尝试失败后回退到搜索引擎
        logger.warning(f"文心一言 API 调用全部失败，回退到搜索引擎: {last_error}")
        return await self._fallback_search(keyword)

    async def _do_query(self, keyword: str) -> str:
        """通过百度千帆 API 获取真实AI回答"""
        if not self.is_configured:
            logger.warning("百度千帆 API Key 未配置，回退到搜索引擎")
            return await self._fallback_search(keyword)

        await _rate_limit_wait()

        access_token = await self._get_access_token()
        chat_url = self._chat_url_template.format(
            model=self._default_model,
            token=access_token,
        )

        client = self._get_client()
        payload = {
            "messages": [
                {
                    "role": "user",
                    "content": keyword,
                },
            ],
            "system": (
                "你是一个专业的AI搜索助手。请基于你的知识，"
                "详细回答用户的问题。如果引用了外部来源，"
                "请在回答中标注来源URL或出处名称。"
            ),
            "temperature": 0.7,
            "max_output_tokens": 2000,
        }

        response = await client.post(chat_url, json=payload)

        if response.status_code == 429:
            retry_after = int(response.headers.get("Retry-After", "10"))
            logger.warning(f"百度千帆 API 限流，等待 {retry_after}s 后重试")
            await asyncio.sleep(retry_after)
            raise RuntimeError(f"百度千帆 API 限流")

        if response.status_code != 200:
            error_body = response.text[:500]
            raise RuntimeError(
                f"百度千帆 API 返回错误 {response.status_code}: {error_body}"
            )

        data = response.json()

        # 检查API错误码
        error_code = data.get("error_code")
        if error_code:
            error_msg = data.get("error_msg", "未知错误")
            raise RuntimeError(f"百度千帆 API 错误 {error_code}: {error_msg}")

        content = data.get("result", "")
        if not content:
            raise RuntimeError("百度千帆 API 返回空内容")

        # 标记数据来源为AI平台
        result = f"[data_source: ai_platform]\n{content}"
        logger.info(f"文心一言 API 调用成功，返回 {len(content)} 字符")
        return result

    async def _fallback_search(self, keyword: str) -> str:
        """回退到搜索引擎模式，标记数据来源"""
        content = await fetch_search_content(self.platform_name, keyword)
        return f"[data_source: search_engine]\n{content}"

    async def close(self):
        """清理资源"""
        if self._client and not self._client.is_closed:
            await self._client.aclose()
            self._client = None
