from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, Body, Depends, HTTPException, Query, status
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext, get_current_user, get_db, require_admin_user
from app.core.agent_enums import AgentName, AgentRunSource
from app.schemas.common import ErrorResponse
from app.schemas.knowledge import (
    KnowledgeActionResponse,
    KnowledgeDocumentDetailRead,
    KnowledgeLibraryRead,
    KnowledgeOnlyOfficeCallbackRead,
    KnowledgeOnlyOfficeCallbackWrite,
    KnowledgeOnlyOfficeConfigRead,
    LlmWikiDocumentDetailRead,
    LlmWikiIndexRead,
    LlmWikiSummaryUpdateWrite,
    LlmWikiSyncTaskRead,
    LlmWikiSyncWrite,
)
from app.services.agent_runs import AgentRunService
from app.services.knowledge import (
    KnowledgeService,
)
from app.services.knowledge_onlyoffice_callback import (
    handle_onlyoffice_callback,
    resolve_onlyoffice_content,
)
from app.services.knowledge_onlyoffice_security import (
    OnlyOfficeReplayError,
    OnlyOfficeSecurityError,
)
from app.services.knowledge_sync import KnowledgeSyncDispatchService

router = APIRouter(prefix="/knowledge")


@router.get(
    "/library",
    response_model=KnowledgeLibraryRead,
    summary="查询知识库目录",
    description="返回固定知识库目录与当前已上传文档列表。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        }
    },
)
def get_knowledge_library(
    current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
    db: Annotated[Session, Depends(get_db)],
) -> KnowledgeLibraryRead:
    return KnowledgeService(db=db, tenant_id=current_user.tenant_id).list_library()


@router.get(
    "/llm-wiki",
    response_model=LlmWikiIndexRead,
    summary="查询知识索引兼容视图",
    description="兼容旧前端字段。当前版本已切换到 LightRAG，不再暴露旧 LLM Wiki 草稿索引。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "只有管理员可以查看索引信息。",
        },
    },
)
def get_llm_wiki_index(
    current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
    db: Annotated[Session, Depends(get_db)],
) -> LlmWikiIndexRead:
    run_service = AgentRunService(db)
    sync_runs = [
        item
        for item in run_service.list_runs_for_tenant(
            tenant_id=current_user.tenant_id,
            agent=AgentName.HERMES.value,
            limit=200,
        )
        if str(item.route_json.get("job_type") or "").strip() == "knowledge_index_sync"
    ]
    return LlmWikiIndexRead(documents=[], sync_run_count=len(sync_runs))


@router.get(
    "/llm-wiki/documents/{document_id}",
    response_model=LlmWikiDocumentDetailRead,
    summary="读取旧版 LLM Wiki 文档结果",
    description="兼容旧接口。当前版本知识库已切换到 LightRAG，不再提供旧 LLM Wiki 文档明细。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "只有管理员可以查看索引信息。",
        },
        status.HTTP_410_GONE: {
            "model": ErrorResponse,
            "description": "旧版 LLM Wiki 明细已下线。",
        },
    },
)
def get_llm_wiki_document_detail(
    document_id: str,
    _: Annotated[CurrentUserContext, Depends(require_admin_user)],
) -> LlmWikiDocumentDetailRead:
    raise HTTPException(
        status_code=status.HTTP_410_GONE,
        detail=(
            f"文档 {document_id} 的旧版 LLM Wiki 明细已下线。"
            "当前版本使用 LightRAG 进行知识索引与检索，不再提供该兼容明细。"
        ),
    )


@router.patch(
    "/llm-wiki/documents/{document_id}",
    response_model=LlmWikiDocumentDetailRead,
    summary="更新旧版 LLM Wiki 知识总结",
    description="兼容旧接口。当前版本不再支持修改旧版 LLM Wiki 草稿。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "只有管理员可以修改索引信息。",
        },
        status.HTTP_410_GONE: {
            "model": ErrorResponse,
            "description": "旧版 LLM Wiki 编辑能力已下线。",
        },
    },
)
def update_llm_wiki_document_summary(
    document_id: str,
    payload: LlmWikiSummaryUpdateWrite,
    _: Annotated[CurrentUserContext, Depends(require_admin_user)],
) -> LlmWikiDocumentDetailRead:
    del payload
    raise HTTPException(
        status_code=status.HTTP_410_GONE,
        detail=(
            f"文档 {document_id} 的旧版 LLM Wiki 草稿编辑能力已下线。"
            "当前版本请通过重新同步知识库来更新 LightRAG 索引。"
        ),
    )


@router.post(
    "/sync",
    response_model=LlmWikiSyncTaskRead,
    summary="????????",
    description="??????????? LightRAG ???????",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "????????????",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "??????????????",
        },
    },
)
@router.post(
    "/llm-wiki/sync",
    response_model=LlmWikiSyncTaskRead,
    summary="????????????",
    description="??????????????? LightRAG ???????",
)
def sync_knowledge_library(
    payload: LlmWikiSyncWrite,
    current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
    db: Annotated[Session, Depends(get_db)],
) -> LlmWikiSyncTaskRead:
    try:
        result = KnowledgeSyncDispatchService(db).queue_sync(
            current_user=current_user,
            folder=payload.folder,
            document_ids=payload.document_ids,
            source=AgentRunSource.USER_MESSAGE.value,
            force=payload.force,
            changed_only=not payload.force,
        )
        return LlmWikiSyncTaskRead(
            ok=True,
            agent_run_id=result.agent_run_id,
            folder=result.folder,
            document_ids=result.document_ids,
            queued_at=result.queued_at,
            status=result.status,
            summary=result.summary,
        )
    except Exception as exc:
        if isinstance(exc, ValueError):
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc
        if isinstance(exc, FileNotFoundError):
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=str(exc),
        ) from exc

@router.get(
    "/documents/{document_id}",
    response_model=KnowledgeDocumentDetailRead,
    summary="读取知识库文档详情",
    description="返回单个知识库文档的元信息、预览类型和预览内容。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def get_knowledge_document(
    document_id: str,
    current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
    db: Annotated[Session, Depends(get_db)],
) -> KnowledgeDocumentDetailRead:
    try:
        return KnowledgeService(
            db=db,
            tenant_id=current_user.tenant_id,
        ).get_document_detail(document_id)
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc


@router.get(
    "/documents/{document_id}/onlyoffice-config",
    response_model=KnowledgeOnlyOfficeConfigRead,
    summary="读取 ONLYOFFICE 预览配置",
    description="为支持的 Office 文档生成 ONLYOFFICE 前端配置和临时访问令牌。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "ONLYOFFICE 未启用、配置不完整或文件格式不支持。",
        },
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def get_knowledge_document_onlyoffice_config(
    document_id: str,
    current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
    db: Annotated[Session, Depends(get_db)],
    editable: Annotated[
        bool,
        Query(description="是否申请租户知识文档编辑会话；默认仅预览。"),
    ] = False,
) -> KnowledgeOnlyOfficeConfigRead:
    try:
        return KnowledgeService(
            db=db,
            tenant_id=current_user.tenant_id,
        ).build_onlyoffice_config(
            document_id,
            current_user,
            editable=editable,
        )
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc


@router.post(
    "/documents",
    response_model=KnowledgeDocumentDetailRead,
    status_code=status.HTTP_201_CREATED,
    summary="上传知识库文档",
    description="上传原始文件二进制内容到指定知识库目录。已有同名文件会覆盖并提升版本号。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "目录、文件名或文件内容不合法。",
        },
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "只有管理员可以上传知识库文件。",
        },
    },
)
def upload_knowledge_document(
    content: Annotated[
        bytes,
        Body(
            media_type="application/octet-stream",
            description="待上传的文件二进制内容。",
        ),
    ],
    folder: Annotated[str, Query(min_length=1, description="目标知识库目录名称。")],
    filename: Annotated[str, Query(min_length=1, description="原始文件名。")],
    current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
    db: Annotated[Session, Depends(get_db)],
) -> KnowledgeDocumentDetailRead:
    try:
        return KnowledgeService(
            db=db,
            tenant_id=current_user.tenant_id,
        ).upload_document(folder, filename, content, current_user)
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc


@router.delete(
    "/documents/{document_id}",
    response_model=KnowledgeActionResponse,
    summary="删除知识库文档",
    description="删除知识库文档及其索引记录。",
    responses={
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_403_FORBIDDEN: {
            "model": ErrorResponse,
            "description": "只有管理员可以删除知识库文件。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def delete_knowledge_document(
    document_id: str,
    current_user: Annotated[CurrentUserContext, Depends(require_admin_user)],
    db: Annotated[Session, Depends(get_db)],
) -> KnowledgeActionResponse:
    try:
        KnowledgeService(
            db=db,
            tenant_id=current_user.tenant_id,
        ).delete_document(document_id)
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc

    return KnowledgeActionResponse(detail="知识库文件已删除。")


@router.get(
    "/documents/{document_id}/content",
    response_class=FileResponse,
    summary="下载或预览知识库原文",
    description="根据文档 ID 返回原始文件内容，可用于浏览器内联预览或下载。",
    responses={
        status.HTTP_200_OK: {
            "description": "文件内容。",
            "content": {"application/octet-stream": {}},
        },
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "未提供知识库访问用户头。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def get_knowledge_document_content(
    document_id: str,
    current_user: Annotated[CurrentUserContext, Depends(get_current_user)],
    db: Annotated[Session, Depends(get_db)],
    disposition: Annotated[
        str,
        Query(
            pattern="^(inline|attachment)$",
            description="内容展示方式，支持 `inline` 或 `attachment`。",
        ),
    ] = "inline",
) -> FileResponse:
    try:
        file_path, media_type, filename = KnowledgeService(
            db=db,
            tenant_id=current_user.tenant_id,
        ).get_document_content(document_id)
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc

    _ = disposition
    return FileResponse(file_path, media_type=media_type, filename=filename)


@router.get(
    "/documents/{document_id}/onlyoffice/content",
    response_class=FileResponse,
    summary="读取 ONLYOFFICE 文档源文件",
    description="供 ONLYOFFICE 服务通过短时访问令牌拉取原始文件内容。",
    responses={
        status.HTTP_200_OK: {
            "description": "文件内容。",
            "content": {"application/octet-stream": {}},
        },
        status.HTTP_401_UNAUTHORIZED: {
            "model": ErrorResponse,
            "description": "ONLYOFFICE 访问令牌无效。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def get_knowledge_document_onlyoffice_content(
    document_id: str,
    access_token: Annotated[
        str,
        Query(min_length=1, description="ONLYOFFICE 临时访问令牌。"),
    ],
    db: Annotated[Session, Depends(get_db)],
) -> FileResponse:
    try:
        file_path, media_type, filename = resolve_onlyoffice_content(
            db=db,
            storage_root=None,
            document_id=document_id,
            access_token=access_token,
        )
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc
    except (OnlyOfficeSecurityError, OnlyOfficeReplayError) as exc:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc

    return FileResponse(file_path, media_type=media_type, filename=filename)


@router.post(
    "/documents/{document_id}/onlyoffice/callback",
    response_model=KnowledgeOnlyOfficeCallbackRead,
    summary="接收 ONLYOFFICE 回调",
    description="接收 ONLYOFFICE 文档回写回调，在状态满足要求时更新知识库文档内容。",
    responses={
        status.HTTP_400_BAD_REQUEST: {
            "model": ErrorResponse,
            "description": "回调载荷不合法。",
        },
        status.HTTP_404_NOT_FOUND: {
            "model": ErrorResponse,
            "description": "知识库文件不存在。",
        },
    },
)
def handle_knowledge_document_onlyoffice_callback(
    document_id: str,
    payload: KnowledgeOnlyOfficeCallbackWrite,
    callback_token: Annotated[
        str,
        Query(min_length=1, description="绑定租户与文档的一次性回调会话令牌。"),
    ],
    db: Annotated[Session, Depends(get_db)],
) -> KnowledgeOnlyOfficeCallbackRead:
    try:
        handle_onlyoffice_callback(
            db=db,
            storage_root=None,
            document_id=document_id,
            callback_token=callback_token,
            payload=payload.model_dump(),
        )
    except FileNotFoundError as exc:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="知识库文件不存在。",
        ) from exc
    except OnlyOfficeReplayError as exc:
        raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=str(exc)) from exc
    except OnlyOfficeSecurityError as exc:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=str(exc)) from exc
    except ValueError as exc:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc)) from exc

    return KnowledgeOnlyOfficeCallbackRead()
