# -*- coding: utf-8 -*-
"""
统一 LLM 客户端
===============
为 C1(术语提取)和 C4(后续)提供统一调用入口。
使用 OpenAI 兼容的 /chat/completions 端点,从 doco/.env 读取凭证。
"""

import json
import os
import sys
from pathlib import Path

# ====================================================================
# 凭证 — 优先加载 doco/.env
# ====================================================================

try:
    from dotenv import load_dotenv

    _doco_env = Path(__file__).resolve().parent.parent.parent / ".env"  # doco/.env
    if _doco_env.exists():
        load_dotenv(str(_doco_env), override=True)
except Exception:
    pass

LLM_API_KEY = os.environ.get("LLM_API_KEY", "").strip()
LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "https://api.xiaomimimo.com/v1").strip()
LLM_MODEL = os.environ.get("LLM_MODEL", "mimo-v2.5-pro").strip()


class LLMConfigError(Exception):
    """LLM 配置异常:key 缺失或为空。上层应 catch 此异常做优雅回退。"""

    pass


def _get_client():
    """内部:创建并返回 openai.OpenAI 客户端实例。key 缺失时抛 LLMConfigError。"""
    if not LLM_API_KEY:
        raise LLMConfigError("LLM_API_KEY 未配置或为空")

    try:
        from openai import OpenAI
    except ImportError:
        raise LLMConfigError("openai 库未安装,请执行 pip install openai")

    return OpenAI(api_key=LLM_API_KEY, base_url=LLM_BASE_URL)


def chat(
    messages,
    *,
    model=None,
    temperature=0.0,
    max_tokens=4096,
    thinking=None,
    **kwargs,
) -> str:
    """
    调用 DeepSeek /chat/completions,返回 assistant 的文本内容。

    参数:
        messages: 标准 messages 列表 [{"role": "user", "content": ...}]
        model: 模型名,默认从 LLM_MODEL 环境变量读取(未设置则 mimo-v2.5-pro)
        temperature: 温度,默认 0.0
        max_tokens: 最大输出 token 数,默认 4096
        thinking: None=不传(默认行为),True=开启,False=传 extra_body 关闭 thinking
        **kwargs: 透传给 client.chat.completions.create()(如 top_p 等)

    返回:
        assistant 回复文本(str)

    异常:
        LLMConfigError — key 缺失/openai 未安装
        openai 库自身的认证/网络/API 异常 — 打印原始响应后重新上抛,不吞错
    """
    if model is None:
        model = LLM_MODEL

    client = _get_client()

    create_kwargs: Dict = {
        "model": model,
        "messages": messages,
        "temperature": temperature,
        "max_tokens": max_tokens,
        **kwargs,
    }
    if thinking is False:
        create_kwargs["extra_body"] = {"thinking": {"type": "disabled"}}

    try:
        response = client.chat.completions.create(**create_kwargs)
    except Exception as e:
        # 打印原始错误响应体,不改模型/不回退
        print(f"[LLM ERROR] 模型={model} 调用失败", file=sys.stderr)
        print(f"[LLM ERROR] 异常类型: {type(e).__name__}", file=sys.stderr)
        print(f"[LLM ERROR] 异常消息: {e}", file=sys.stderr)
        # 尝试从 openai 异常中提取原始响应
        if hasattr(e, "response"):
            try:
                body = e.response.json()
                print(f"[LLM ERROR] 原始响应体: {json.dumps(body, ensure_ascii=False)}", file=sys.stderr)
            except Exception:
                print(f"[LLM ERROR] 原始响应体: {e.response.text[:2000]}", file=sys.stderr)
        raise

    return response.choices[0].message.content