from __future__ import annotations

import json
import re
from dataclasses import dataclass, field
from typing import Any, Literal, Protocol
from urllib import error, request

from app.services.session_store import SessionState


SocialCategory = Literal["none", "open_social"]
ShortSocialKind = Literal["greeting", "thanks", "goodbye", "capability"]


@dataclass
class SocialRouteResult:
    category: SocialCategory
    reason: str
    short_kind: ShortSocialKind | None = None


@dataclass
class SocialReplyResult:
    text: str
    backend: str
    model_name: str
    error_message: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


class SocialResponder(Protocol):
    def reply(self, text: str, session: SessionState) -> SocialReplyResult:
        ...


class SocialRouter:
    _SHORT_SOCIAL_PATTERNS: dict[ShortSocialKind, tuple[str, ...]] = {
        "greeting": ("你好", "您好", "嗨", "哈喽", "hi", "hello", "在吗", "在不在"),
        "thanks": ("谢谢", "谢啦", "多谢", "thanks", "thank you", "辛苦了"),
        "goodbye": ("再见", "拜拜", "回头见", "bye", "goodbye"),
        "capability": (
            "你是谁",
            "你叫什么",
            "你叫什么名字",
            "你叫啥",
            "怎么称呼你",
            "介绍一下你自己",
            "你能做什么",
            "你会什么",
            "你可以做什么",
        ),
    }
    _OPEN_SOCIAL_PATTERNS: tuple[str, ...] = (
        "天气",
        "讲个笑话",
        "笑话",
        "无聊",
        "有点累",
        "有点困",
        "有点烦",
        "开心",
        "不开心",
        "真不错",
        "真好",
        "聊聊天",
        "你觉得",
        "你怎么看",
        "你说呢",
    )
    _OPEN_SOCIAL_REGEXES: tuple[re.Pattern[str], ...] = (
        re.compile(r"今天.*(不错|真好|挺好|真舒服)"),
        re.compile(r"(好|真)热啊"),
        re.compile(r"(好|真)冷啊"),
        re.compile(r"我今天.*(累|困|烦|开心|难过)"),
        re.compile(r".*(怎么样|如何|咋样)[？?]?$"),
    )
    _CAPABILITY_REGEXES: tuple[re.Pattern[str], ...] = (
        re.compile(r"你.*(叫.*名字|叫什么|叫啥)[？?]?$"),
        re.compile(r"(怎么称呼你|介绍一下你自己)[？?]?$"),
        re.compile(r"你.*(能做什么|会什么|可以做什么)[？?]?$"),
    )
    _TASK_KEYWORDS: tuple[str, ...] = (
        "订单",
        "物流",
        "取消",
        "转人工",
        "导航",
        "去",
        "到",
        "空调",
        "温度",
        "调到",
        "播放",
        "音乐",
        "歌曲",
        "车窗",
        "座椅",
        "后视镜",
        "灯光",
        "除雾",
        "确认",
        "不用",
    )

    def route(self, text: str, session: SessionState) -> SocialRouteResult:
        normalized = self._normalize(text)
        if not normalized:
            return SocialRouteResult(category="none", reason="empty text")
        if self._looks_like_task(normalized):
            return SocialRouteResult(category="none", reason="contains task keywords")
        for short_kind, patterns in self._SHORT_SOCIAL_PATTERNS.items():
            if any(pattern in normalized for pattern in patterns):
                return SocialRouteResult(
                    category="open_social",
                    short_kind=short_kind,
                    reason=f"matched social pattern routed to llm: {short_kind}",
                )
        if any(regex.search(normalized) for regex in self._CAPABILITY_REGEXES):
            return SocialRouteResult(
                category="open_social",
                short_kind="capability",
                reason="matched capability social regex routed to llm",
            )
        if any(pattern in normalized for pattern in self._OPEN_SOCIAL_PATTERNS):
            return SocialRouteResult(category="open_social", reason="matched open social phrase")
        if any(regex.search(normalized) for regex in self._OPEN_SOCIAL_REGEXES):
            return SocialRouteResult(category="open_social", reason="matched open social regex")
        if session.context_memory.get("last_dialog_mode") == "open_social" and len(normalized) <= 14:
            return SocialRouteResult(category="open_social", reason="follow-up to previous open social turn")
        return SocialRouteResult(category="none", reason="no social pattern matched")

    def _normalize(self, text: str) -> str:
        return re.sub(r"\s+", "", text.strip().lower())

    def _looks_like_task(self, normalized: str) -> bool:
        if any(keyword in normalized for keyword in self._TASK_KEYWORDS):
            return True
        return bool(re.match(r"^(查|帮我查|打开|关闭|设置|调|导航|播放|取消|转)(.+)", normalized))


class DashScopeSocialResponder:
    def __init__(
        self,
        base_url: str,
        api_key: str,
        model_name: str,
        timeout_seconds: float = 6.0,
    ) -> None:
        self._base_url = base_url.rstrip("/")
        self._api_key = api_key
        self._model_name = model_name
        self._timeout_seconds = timeout_seconds

    def reply(self, text: str, session: SessionState) -> SocialReplyResult:
        if not self._base_url or not self._api_key or not self._model_name:
            return SocialReplyResult(
                text="可以和你聊两句，你也可以继续让我处理查询或控制。",
                backend="local-fallback",
                model_name="social-fallback",
                error_message="social responder is not configured",
            )

        payload = {
            "model": self._model_name,
            "temperature": 0.6,
            "enable_thinking": False,
            "max_tokens": 120,
            "messages": [
                {
                    "role": "system",
                    "content": (
                        "你是智能座舱助手，负责处理所有闲聊、问候、身份问答、能力介绍和开放聊天。"
                        "请用自然、口语化、简短的中文回答，优先 1-3 句，总长度尽量不超过 50 个字。"
                        "如果用户在打招呼、问你是谁、问你叫什么名字、问你会什么，请直接自然回答，不要像固定菜单。"
                        "可以结合用户上下文自然接话，但不要过度展开。"
                        "不要编造已经执行了任何车辆或客服动作。"
                        "不要输出 JSON，不要长篇解释。"
                    ),
                },
                {
                    "role": "user",
                    "content": json.dumps(
                        {
                            "text": text,
                            "context": {
                                "last_user_text": session.last_user_text,
                                "last_agent_text": session.last_agent_text,
                                "current_intent": session.current_intent,
                                "status": session.status,
                            },
                        },
                        ensure_ascii=False,
                    ),
                },
            ],
        }
        req = request.Request(
            self._endpoint(),
            data=json.dumps(payload).encode("utf-8"),
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {self._api_key}",
            },
            method="POST",
        )
        try:
            with request.urlopen(req, timeout=self._timeout_seconds) as response:
                data = json.loads(response.read().decode("utf-8"))
        except (error.URLError, TimeoutError, ValueError) as exc:
            return SocialReplyResult(
                text="是啊，听起来今天状态不错。",
                backend="local-fallback",
                model_name="social-fallback",
                error_message=str(exc),
            )

        content = self._extract_content(data)
        if not content:
            return SocialReplyResult(
                text="可以和你聊两句，你也可以继续说说看。",
                backend="local-fallback",
                model_name="social-fallback",
                error_message="empty social response",
            )
        return SocialReplyResult(
            text=content,
            backend="dashscope",
            model_name=self._model_name,
        )

    def _endpoint(self) -> str:
        if self._base_url.endswith("/chat/completions"):
            return self._base_url
        return f"{self._base_url}/chat/completions"

    def _extract_content(self, payload: dict[str, Any]) -> str:
        choices = payload.get("choices")
        if not isinstance(choices, list) or not choices:
            return ""
        message = choices[0].get("message", {})
        content = message.get("content", "")
        if isinstance(content, list):
            parts = [
                str(item.get("text", "")).strip()
                for item in content
                if isinstance(item, dict) and item.get("type") == "text"
            ]
            return "".join(parts).strip()
        return str(content).strip()
