"""QA (智能问答) endpoint — SSE streaming answer with graph + ES retrieval.

智能问答接口模块。
基于知识图谱 + ES 混合检索进行上下文召回，通过 LLM 生成回答，
以 SSE 流式推送给前端，支持多轮对话和指定文档上下文。
"""

from __future__ import annotations

import json
from typing import Annotated, AsyncIterator

from fastapi import APIRouter, Depends
from fastapi.responses import StreamingResponse

from app.api.deps import (
    UserContext,
    get_current_user,
    get_embedding_client,
    get_es_client,
    get_llm_client,
    get_mysql_client,
    get_neo4j_client,
    get_redis_client,
)
from app.api.schemas.qa import QARequest, QASearchDocument, QASearchRequest, QASearchResponse
from app.api.schemas.research import ResearchChunk
from app.core.embedding import EmbeddingService
from app.core.graph_query_service import GraphQueryService
from app.core.permission import PermissionService
from app.core.research_engine import ResearchEngine
from app.infrastructure.embedding_client import EmbeddingClient
from app.infrastructure.es_client import ESClient
from app.infrastructure.llm_client import LLMClient
from app.infrastructure.mysql_client import MySQLClient
from app.infrastructure.neo4j_client import Neo4jClient
from app.infrastructure.redis_client import RedisClient
from app.infrastructure.session_store import build_research_session_store
from app.utils.logger import get_logger

logger = get_logger(__name__)

router = APIRouter(prefix="/qa", tags=["qa"])


async def _sse_stream(
    gen: AsyncIterator[ResearchChunk],
) -> AsyncIterator[str]:
    """将 ResearchChunk 异步生成器转换为 SSE data 帧流。"""
    async for chunk in gen:
        payload = json.dumps(chunk.model_dump(exclude_none=True), ensure_ascii=False)
        yield f"data: {payload}\n\n"


@router.post(
    "",
    summary="智能问答 (SSE 流式)",
    response_description="Server-Sent Events stream of ResearchChunk objects",
)
async def qa(
    body: QARequest,
    user: Annotated[UserContext, Depends(get_current_user)],
    es_client: Annotated[ESClient, Depends(get_es_client)],
    neo4j_client: Annotated[Neo4jClient, Depends(get_neo4j_client)],
    redis_client: Annotated[RedisClient, Depends(get_redis_client)],
    mysql_client: Annotated[MySQLClient | None, Depends(get_mysql_client)],
    embedding_client: Annotated[EmbeddingClient, Depends(get_embedding_client)],
    llm_client: Annotated[LLMClient, Depends(get_llm_client)],
) -> StreamingResponse:
    """接收用户问题，经 RAG 检索增强后由 LLM 生成回答，以 SSE 流式返回。"""
    logger.info(
        "qa_request",
        user_id=user.user_id,
        question_len=len(body.question),
        session_id=body.session_id,
        seed_doc_count=len(body.seed_doc_ids or []),
    )

    perm_service = PermissionService(redis_client=redis_client)
    perm = await perm_service.resolve(user)

    embedding_svc = EmbeddingService(embedding_client)
    graph_svc = GraphQueryService(neo4j_client)
    session_store = build_research_session_store(
        redis_client=redis_client,
        mysql_client=mysql_client,
    )
    engine = ResearchEngine(
        es_client=es_client,
        embedding_service=embedding_svc,
        graph_service=graph_svc,
        llm_client=llm_client,
        session_store=session_store,
    )

    gen = engine.qa(
        body.question,
        perm,
        session_id=body.session_id,
        seed_doc_ids=body.seed_doc_ids,
    )

    return StreamingResponse(
        _sse_stream(gen),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )


@router.post(
    "/search",
    summary="QA 材料检索（仅检索，不生成回答）",
    response_model=QASearchResponse,
)
async def qa_search(
    body: QASearchRequest,
    user: Annotated[UserContext, Depends(get_current_user)],
    es_client: Annotated[ESClient, Depends(get_es_client)],
    neo4j_client: Annotated[Neo4jClient, Depends(get_neo4j_client)],
    redis_client: Annotated[RedisClient, Depends(get_redis_client)],
    mysql_client: Annotated[MySQLClient | None, Depends(get_mysql_client)],
    embedding_client: Annotated[EmbeddingClient, Depends(get_embedding_client)],
    llm_client: Annotated[LLMClient, Depends(get_llm_client)],
) -> QASearchResponse:
    """仅执行 QA 检索流水线（关键词提取 → 图谱规划 → 多路召回 → 合并），
    返回结构化的文档列表和格式化的参考材料文本，不调用 LLM 生成回答。

    适用于三方系统需要获取检索材料后自行处理的场景。
    """
    logger.info(
        "qa_search_request",
        user_id=user.user_id,
        question_len=len(body.question),
        seed_doc_count=len(body.seed_doc_ids or []),
    )

    perm_service = PermissionService(redis_client=redis_client)
    perm = await perm_service.resolve(user)

    embedding_svc = EmbeddingService(embedding_client)
    graph_svc = GraphQueryService(neo4j_client)
    session_store = build_research_session_store(
        redis_client=redis_client,
        mysql_client=mysql_client,
    )
    engine = ResearchEngine(
        es_client=es_client,
        embedding_service=embedding_svc,
        graph_service=graph_svc,
        llm_client=llm_client,
        session_store=session_store,
    )

    result = await engine.qa_search(
        body.question,
        perm,
        seed_doc_ids=body.seed_doc_ids,
    )

    documents = [QASearchDocument(**doc) for doc in result["documents"]]

    return QASearchResponse(
        question=body.question,
        keywords=result["keywords"],
        intent=result["intent"],
        total=len(documents),
        documents=documents,
        context_text=result["context_text"],
        graph_evidence_text=result["graph_evidence_text"],
        guide_evidence_text=result["guide_evidence_text"],
    )