from config.logger import setup_logging
from http import HTTPStatus
import dashscope
from dashscope import Application
from core.providers.llm.base import LLMProviderBase
from core.utils.util import check_model_key
import time

TAG = __name__
logger = setup_logging()


class LLMProvider(LLMProviderBase):
    def __init__(self, config):
        self.api_key = config["api_key"]
        self.app_id = config["app_id"]
        self.base_url = config.get("base_url")
        self.is_No_prompt = config.get("is_no_prompt")
        self.memory_id = config.get("ali_memory_id")
        self.streaming_chunk_size = config.get("streaming_chunk_size", 3)  # 每次流式返回的字符数
        check_model_key("AliBLLLM", self.api_key)

    def response(self, session_id, dialogue):
        try:
            # 处理dialogue
            if self.is_No_prompt:
                dialogue.pop(0)
                logger.bind(tag=TAG).debug(
                    f"【阿里百练API服务】处理后的dialogue: {dialogue}"
                )

            # 构造调用参数
            call_params = {
                "api_key": self.api_key,
                "app_id": self.app_id,
                "session_id": session_id,
                "messages": dialogue,
                # 开启SDK原生流式
                "stream": True,
            }
            if self.memory_id != False:
                # 百练memory需要prompt参数
                prompt = dialogue[-1].get("content")
                call_params["memory_id"] = self.memory_id
                call_params["prompt"] = prompt
                logger.bind(tag=TAG).debug(
                    f"【阿里百练API服务】处理后的prompt: {prompt}"
                )

            # 可选地设置自定义API基地址（若配置为兼容模式URL则忽略）
            if self.base_url and ("/api/" in self.base_url):
                dashscope.base_http_api_url = self.base_url

            responses = Application.call(**call_params)

            # 流式处理（SDK在stream=True时返回可迭代对象；否则返回单次响应对象）
            logger.bind(tag=TAG).debug(
                f"【阿里百练API服务】构造参数: {dict(call_params, api_key='***')}"
            )

            last_text = ""
            try:
                for resp in responses:
                    if resp.status_code != HTTPStatus.OK:
                        logger.bind(tag=TAG).error(
                            f"code={resp.status_code}, message={resp.message}, 请参考文档：https://help.aliyun.com/zh/model-studio/developer-reference/error-code"
                        )
                        continue
                    current_text = getattr(getattr(resp, "output", None), "text", None)
                    if current_text is None:
                        continue
                    # SDK流式为增量覆盖，计算差量输出
                    if len(current_text) >= len(last_text):
                        delta = current_text[len(last_text):]
                    else:
                        # 避免偶发回退
                        delta = current_text
                    if delta:
                        yield delta
                    last_text = current_text
            except TypeError:
                # 非流式回落（一次性返回）
                if responses.status_code != HTTPStatus.OK:
                    logger.bind(tag=TAG).error(
                        f"code={responses.status_code}, message={responses.message}, 请参考文档：https://help.aliyun.com/zh/model-studio/developer-reference/error-code"
                    )
                    yield "【阿里百练API服务响应异常】"
                else:
                    full_text = getattr(getattr(responses, "output", None), "text", "")
                    logger.bind(tag=TAG).info(
                        f"【阿里百练API服务】完整响应长度: {len(full_text)}"
                    )
                    for i in range(0, len(full_text), self.streaming_chunk_size):
                        chunk = full_text[i:i + self.streaming_chunk_size]
                        if chunk:
                            yield chunk

        except Exception as e:
            logger.bind(tag=TAG).error(f"【阿里百练API服务】响应异常: {e}")
            yield "【LLM服务响应异常】"

    def response_with_functions(self, session_id, dialogue, functions=None):
        # 阿里百练当前未支持原生的 function call。为保持兼容，这里回退到普通文本流式输出。
        # 上层会按 (content, tool_calls) 的形式消费，这里始终返回 (token, None)
        logger.bind(tag=TAG).warning(
            "阿里百练未实现原生 function call，已回退为纯文本流式输出"
        )
        for token in self.response(session_id, dialogue):
            yield token, None
