from __future__ import annotations

from dataclasses import dataclass

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.models.ai_config import Prompt
from app.models.user import User
from app.services.feishu_service import FeishuKnowledgeService
from app.services.knowledge_service import KnowledgeAccessService, KnowledgeScope

NO_HIT_ANSWER = "当前知识库中未检索到相关内容，请联系管理员补充相关知识。"


@dataclass(frozen=True)
class RetrievedChunk:
    knowledge_id: int
    knowledge_name: str
    title: str
    content: str
    source_url: str | None = None


@dataclass(frozen=True)
class RagResult:
    question: str
    knowledge_scopes: list[KnowledgeScope]
    chunks: list[RetrievedChunk]
    prompt: str

    @property
    def is_hit(self) -> bool:
        return len(self.chunks) > 0

    @property
    def knowledge_ids(self) -> str:
        return ",".join(str(scope.id) for scope in self.knowledge_scopes)


class RagService:
    @staticmethod
    def build_result(db: Session, user: User, question: str) -> RagResult:
        scopes = KnowledgeAccessService.get_allowed_knowledge(db, user)
        chunks = FeishuKnowledgeService.retrieve(question, scopes, db)
        prompt = PromptService.build_prompt(db, question, chunks)
        return RagResult(question=question, knowledge_scopes=scopes, chunks=chunks, prompt=prompt)


class PromptService:
    _default_prompt = (
        "你是企业飞书知识库 AI 助手。你只能基于提供的知识片段回答，不能编造。"
        "如果知识片段之间有冲突，需要说明不同来源的观点。"
    )

    @classmethod
    def build_prompt(cls, db: Session, question: str, chunks: list[RetrievedChunk]) -> str:
        prompt = cls._load_active_prompt(db)
        context = "\n\n".join(
            f"[知识片段 {index}] 来源：{chunk.knowledge_name} / {chunk.title}\n{chunk.content}"
            for index, chunk in enumerate(chunks, start=1)
        )
        if not context:
            context = "未检索到相关知识片段。"
        return f"{prompt}\n\n{context}\n\n用户问题：{question.strip()}"

    @classmethod
    def _load_active_prompt(cls, db: Session) -> str:
        prompt = db.scalar(
            select(Prompt)
            .order_by(Prompt.updated_at.desc(), Prompt.id.desc())
            .limit(1)
        )
        return prompt.prompt_content if prompt else cls._default_prompt
