from __future__ import annotations

from typing import TYPE_CHECKING, Any

import httpx

from app.core.config import get_settings
from app.services.external_errors import ExternalServiceError
from app.services.knowledge_service import KnowledgeScope

if TYPE_CHECKING:
    from app.services.rag_service import RetrievedChunk


class FeishuKnowledgeService:
    _mock_documents = [
        {
            "title": "一期产品目标",
            "keywords": {"一期", "目标", "问答", "登录", "知识库", "飞书", "用户端", "后台"},
            "content": "一期要交付企业飞书知识库 AI 问答系统，核心包括用户登录、AI 问答、权限内知识库回答和后台管理能力。",
        },
        {
            "title": "权限过滤规则",
            "keywords": {"权限", "授权", "越权", "用户", "知识库", "过期", "禁用"},
            "content": "RAG 检索前必须先过滤用户授权知识库，只允许未禁用、未过期、当前用户有权限的知识库参与回答。",
        },
        {
            "title": "无命中兜底规则",
            "keywords": {"无命中", "兜底", "编造", "检索", "答案", "命中"},
            "content": "当知识库没有命中相关内容时，系统必须固定返回无命中兜底文案。",
        },
        {
            "title": "技术实现边界",
            "keywords": {"模型", "prompt", "大模型", "日志", "sse", "流式", "追溯"},
            "content": "后端需要组装系统 Prompt、检索片段、历史上下文和当前问题，通过 SSE 流式输出，并记录 AI 请求日志。",
        },
    ]

    @classmethod
    def retrieve(cls, question: str, scopes: list[KnowledgeScope]) -> list["RetrievedChunk"]:
        if not scopes:
            return []

        settings = get_settings()
        if settings.feishu_mock_enabled:
            return cls._retrieve_mock(question, scopes)
        return cls._retrieve_remote(question, scopes)

    @classmethod
    def _retrieve_mock(cls, question: str, scopes: list[KnowledgeScope]) -> list["RetrievedChunk"]:
        from app.services.rag_service import RetrievedChunk

        normalized_question = question.lower()
        matched_documents = []
        for document in cls._mock_documents:
            if any(keyword.lower() in normalized_question for keyword in document["keywords"]):
                matched_documents.append(document)

        if not matched_documents:
            return []

        primary_scope = scopes[0]
        return [
            RetrievedChunk(
                knowledge_id=primary_scope.id,
                knowledge_name=primary_scope.name,
                title=document["title"],
                content=document["content"],
                source_url=None,
            )
            for document in matched_documents[:3]
        ]

    @classmethod
    def _retrieve_remote(cls, question: str, scopes: list[KnowledgeScope]) -> list["RetrievedChunk"]:
        from app.services.rag_service import RetrievedChunk

        settings = get_settings()
        if not settings.feishu_search_url:
            raise ExternalServiceError("飞书检索地址未配置", provider="feishu")

        payload = {
            "query": question,
            "knowledgeScopes": [
                {
                    "id": scope.id,
                    "spaceId": scope.feishu_space_id,
                    "nodeId": scope.feishu_node_id,
                    "name": scope.name,
                }
                for scope in scopes
            ],
        }
        data = cls._post_with_retry(settings.feishu_search_url, payload)
        chunks = data.get("chunks", [])
        return [
            RetrievedChunk(
                knowledge_id=int(item.get("knowledgeId") or 0),
                knowledge_name=str(item.get("knowledgeName") or "飞书知识库"),
                title=str(item.get("title") or "未命名片段"),
                content=str(item.get("content") or ""),
                source_url=item.get("sourceUrl"),
            )
            for item in chunks
            if item.get("content")
        ]

    @staticmethod
    def _post_with_retry(url: str, payload: dict[str, Any]) -> dict[str, Any]:
        settings = get_settings()
        last_error: Exception | None = None
        for _ in range(settings.feishu_retry_count + 1):
            try:
                response = httpx.post(url, json=payload, timeout=settings.feishu_timeout_seconds)
                response.raise_for_status()
                data = response.json()
                if not isinstance(data, dict):
                    raise ExternalServiceError("飞书检索响应格式不正确", provider="feishu")
                return data
            except (httpx.HTTPError, ValueError, ExternalServiceError) as exc:
                last_error = exc
        raise ExternalServiceError(f"飞书检索失败：{last_error}", provider="feishu")
