"""大模型抽象层"""

from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Optional

PROVIDER_CONFIGS = {
    "deepseek": {"base_url": "https://api.deepseek.com", "default_model": "deepseek-chat"},
    "dashscope": {"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "default_model": "qwen-plus"},
    "moonshot": {"base_url": "https://api.moonshot.cn/v1", "default_model": "moonshot-v1-8k"},
    "zhipu": {"base_url": "https://open.bigmodel.cn/api/paas/v4", "default_model": "glm-4-flash"},
    "openai": {"base_url": "https://api.openai.com/v1", "default_model": "gpt-4o-mini"},
}

try:
    from openai import AsyncOpenAI
    _HAS_OPENAI = True
except ImportError:
    _HAS_OPENAI = False
    AsyncOpenAI = None


@dataclass
class LLMConfig:
    provider: str = "deepseek"
    api_key: str = ""
    model: Optional[str] = None
    base_url: Optional[str] = None
    temperature: float = 0.3
    max_tokens: int = 2000

    @property
    def resolved_base_url(self) -> str:
        if self.base_url:
            return self.base_url
        return PROVIDER_CONFIGS.get(self.provider, {}).get("base_url", "https://api.deepseek.com")

    @property
    def resolved_model(self) -> str:
        if self.model:
            return self.model
        return PROVIDER_CONFIGS.get(self.provider, {}).get("default_model", "deepseek-chat")


class LLMClient:
    def __init__(self, config: LLMConfig):
        self.config = config
        self._client = None
        self._stub_mode = not config.api_key or not _HAS_OPENAI

        if not self._stub_mode:
            self._client = AsyncOpenAI(
                api_key=config.api_key,
                base_url=config.resolved_base_url,
            )

    async def chat(
        self,
        system_prompt: str,
        user_prompt: str,
        temperature: Optional[float] = None,
    ) -> str:
        if self._stub_mode:
            return "Emotion trending down over the past 2 weeks."

        response = await self._client.chat.completions.create(
            model=self.config.resolved_model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_prompt},
            ],
            temperature=temperature or self.config.temperature,
            max_tokens=self.config.max_tokens,
        )
        return response.choices[0].message.content or ""

    async def chat_json(
        self,
        system_prompt: str,
        user_prompt: str,
    ) -> str:
        if self._stub_mode:
            return self._stub_json(user_prompt)

        json_hint = (
            "\n\nYou must return valid JSON only. "
            "No other text, no markdown."
        )
        return await self.chat(
            system_prompt + json_hint,
            user_prompt,
            temperature=0.1,
        )

    def _stub_json(self, prompt: str) -> str:
        indices = re.findall(r"\[(\d+)\]", prompt)

        if "score" in prompt.lower() or "sentiment" in prompt.lower():
            items = []
            for i, idx in enumerate(indices):
                s = round(0.6 - i * 0.15, 2)
                s = max(-1.0, min(1.0, s))
                items.append('{"index": ' + idx + ', "score": ' + str(s) + "}")
            return "[" + ", ".join(items) + "]"

        if "topic" in prompt.lower():
            topics = ["work_pressure", "overtime", "fatigue", "japanese", "food"]
            items = []
            for i, idx in enumerate(indices):
                t = topics[i % len(topics)]
                items.append('{"index": ' + idx + ', "topics": ["' + t + '"]}')
            return "[" + ", ".join(items) + "]"

        if "goal" in prompt.lower():
            items = []
            for idx in indices:
                items.append('{"index": ' + idx + ', "goals": []}')
            return "[" + ", ".join(items) + "]"

        return "[]"
