"""LLM-based 问题生成 — 为文档段落生成相关问题，提升检索召回率。

参考 memory/contextual_retrieval.py 的 ContextualChunker 模式：
- 使用 LLM gateway 生成问题
- Prompt 模板化
- 失败时降级（返回空列表，不抛异常）

生成的问题可作为 chunk 的 metadata.questions 字段，在索引时与 chunk 内容
一同嵌入，提升"问题→段落"的检索召回率（HyDE 反向模式）。
"""

from __future__ import annotations

import hashlib
import logging
import re

from pydantic import BaseModel, ConfigDict

logger = logging.getLogger(__name__)


class GeneratedQuestion(BaseModel):
    """生成的问题条目。"""

    model_config = ConfigDict()

    question: str
    chunk_id: str
    document_id: str


# Prompt 模板 — 指示 LLM 为给定 chunk 生成可被该 chunk 回答的问题
QUESTION_GEN_PROMPT_TEMPLATE = """\
请为以下文本片段生成 {n} 个问题，要求：
1. 每个问题都能直接从该片段中找到答案
2. 问题应覆盖片段中的关键信息点
3. 问题应简洁、自然，符合用户提问习惯
4. 每行一个问题，不要编号，不要前缀

文本片段：
{chunk}

问题（每行一个）：
"""


# ponytail: 解析 LLM 输出为问题列表 — 按行切分，过滤空行和编号前缀
# 升级路径：若 LLM 输出格式不稳定，可改用 JSON 模式（要求 LLM 输出 JSON 数组）
_NUMBER_PREFIX_RE = re.compile(r"^\s*\d+[\.\)、\]]\s*")


class QuestionGenerator:
    """问题生成器 — 为每个 chunk 生成相关问题。

    Args:
        llm_gateway: LLM Gateway 实例（需实现 async chat(messages, model) -> response）
        max_questions_per_chunk: 每个 chunk 生成的问题数上限
        model: LLM 模型名（默认 "default"）
        cache: 是否启用缓存（避免对同一 chunk 重复调用 LLM）
    """

    def __init__(
        self,
        llm_gateway: object | None = None,
        max_questions_per_chunk: int = 3,
        model: str = "default",
        cache: bool = True,
    ) -> None:
        self._llm_gateway = llm_gateway
        self._max_questions = max_questions_per_chunk
        self._model = model
        self._cache_enabled = cache
        self._cache: dict[str, list[str]] = {}

    async def generate(
        self,
        chunks: list[dict[str, object]],
        document_context: str = "",
    ) -> list[GeneratedQuestion]:
        """为每个 chunk 生成相关问题。

        Args:
            chunks: chunk 字典列表，每个字典需包含 id、content、document_id 字段
            document_context: 完整文档内容（可选，用于提供额外上下文）

        Returns:
            生成的问题列表（GeneratedQuestion）。无 LLM 或失败时返回空列表。
        """
        if not chunks:
            return []

        if not self._llm_gateway:
            logger.info("No LLM gateway configured, skipping question generation")
            return []

        results: list[GeneratedQuestion] = []
        for chunk in chunks:
            chunk_id = str(chunk.get("id", ""))
            content = str(chunk.get("content", ""))
            document_id = str(chunk.get("document_id", ""))

            if not content.strip():
                continue

            questions = await self._generate_for_chunk(content, document_context)
            for q in questions:
                results.append(
                    GeneratedQuestion(
                        question=q,
                        chunk_id=chunk_id,
                        document_id=document_id,
                    )
                )

        return results

    async def _generate_for_chunk(
        self,
        chunk_content: str,
        document_context: str,
    ) -> list[str]:
        """为单个 chunk 生成问题。

        Args:
            chunk_content: chunk 文本内容
            document_context: 完整文档内容（当前未使用，保留以供未来扩展）

        Returns:
            问题字符串列表。失败时返回空列表。
        """
        # 缓存检查
        cache_key = self._make_cache_key(chunk_content)
        if self._cache_enabled and cache_key in self._cache:
            return self._cache[cache_key]

        # 截断超长 chunk — 避免 prompt 过长
        chunk_preview = chunk_content[:2000] if len(chunk_content) > 2000 else chunk_content

        prompt = QUESTION_GEN_PROMPT_TEMPLATE.format(
            n=self._max_questions,
            chunk=chunk_preview,
        )

        try:
            response = await self._llm_gateway.chat(
                messages=[{"role": "user", "content": prompt}],
                model=self._model,
            )
            content = response.content.strip()
            questions = self._parse_questions(content)
        except Exception as e:
            logger.warning("Question generation failed for chunk: %s", e)
            questions = []

        if self._cache_enabled:
            self._cache[cache_key] = questions

        return questions

    @staticmethod
    def _parse_questions(raw_output: str) -> list[str]:
        """解析 LLM 输出为问题列表 — 按行切分，过滤空行和编号前缀。

        Args:
            raw_output: LLM 原始输出文本

        Returns:
            问题字符串列表（最多 max_questions 个）。
        """
        lines: list[str] = []
        for line in raw_output.splitlines():
            line = line.strip()
            if not line:
                continue
            # 去除编号前缀（如 "1. "、"2) "、"3、 "）
            line = _NUMBER_PREFIX_RE.sub("", line).strip()
            if line:
                lines.append(line)
        return lines

    @staticmethod
    def _make_cache_key(chunk_content: str) -> str:
        """生成缓存键 — 基于 chunk 内容的 SHA256 哈希。"""
        return hashlib.sha256(chunk_content.encode()).hexdigest()[:16]

    def clear_cache(self) -> None:
        """清除问题生成缓存。"""
        self._cache.clear()


__all__ = ["GeneratedQuestion", "QuestionGenerator"]
