from __future__ import annotations

import json
from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, Query, status
from sqlalchemy import delete, func, select
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.dependencies import get_current_admin
from app.core.responses import api_success
from app.models.admin import Admin
from app.models.knowledge import (
    HumanAttentionHistory,
    HumanAttentionRecord,
    Knowledge,
    KnowledgeRetrievalCandidate,
    KnowledgeRetrievalLog,
)
from app.schemas.knowledge import AttentionUpdateRequest
from app.services.admin_service import OperationLogService
from app.models.logs import LogRetentionPolicy
from app.services.maintenance_service import MaintenanceService
from app.api.pagination import page_result

router = APIRouter()


@router.get("/retrieval-log/list")
def retrieval_logs(
    sessionId: int | None = Query(default=None),
    knowledgeId: int | None = Query(default=None),
    knowledgeCalled: bool | None = Query(default=None),
    statusValue: str = Query(default="", alias="status"),
    attentionCreated: bool | None = Query(default=None),
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=50, ge=1, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    query = select(KnowledgeRetrievalLog).order_by(KnowledgeRetrievalLog.id.desc())
    if sessionId is not None:
        query = query.where(KnowledgeRetrievalLog.session_id == sessionId)
    if knowledgeId is not None:
        query = query.where(KnowledgeRetrievalLog.selected_knowledge_ids.like(f"%{knowledgeId}%"))
    if knowledgeCalled is not None:
        query = query.where(KnowledgeRetrievalLog.knowledge_called == (1 if knowledgeCalled else 0))
    if statusValue:
        query = query.where(KnowledgeRetrievalLog.status == statusValue)
    if attentionCreated is not None:
        query = query.where(KnowledgeRetrievalLog.attention_created == (1 if attentionCreated else 0))
    total = db.scalar(select(func.count()).select_from(query.order_by(None).subquery())) or 0
    rows = db.scalars(query.offset((page - 1) * pageSize).limit(pageSize)).all()
    return api_success(page_result([_retrieval_dict(item, detail=False) for item in rows], total=total, page=page, page_size=pageSize))


@router.post("/retrieval-log/cleanup/estimate")
def estimate_cleanup(payload: dict, db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    before = _cleanup_before(payload)
    count = db.scalar(select(func.count()).select_from(KnowledgeRetrievalLog).where(KnowledgeRetrievalLog.created_at < before)) or 0
    return api_success({"before": before, "estimatedCount": count})


@router.post("/retrieval-log/cleanup")
def cleanup_logs(payload: dict, db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    before = _cleanup_before(payload)
    total = MaintenanceService.delete_retrieval_logs(db, before)
    OperationLogService.write(db, admin_id=current_admin.id, module="retrieval_log", action=f"cleanup:{total}", target_id=None)
    db.commit()
    return api_success({"deleted": total, "before": before})


@router.get("/retrieval-log/retention")
def get_retention(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    policy = db.scalar(select(LogRetentionPolicy).order_by(LogRetentionPolicy.id).limit(1))
    return api_success({"enabled": bool(policy and policy.enabled), "retentionDays": policy.retention_days if policy else None, "lastRunAt": policy.last_run_at if policy else None, "lastResult": policy.last_result if policy else None})


@router.put("/retrieval-log/retention")
def set_retention(payload: dict, db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    days = payload.get("retentionDays")
    if days is not None and (not isinstance(days, int) or days < 1):
        raise HTTPException(status_code=400, detail="保留天数必须为正整数，永久保留请传 null")
    policy = db.scalar(select(LogRetentionPolicy).order_by(LogRetentionPolicy.id).limit(1)) or LogRetentionPolicy()
    policy.retention_days, policy.enabled, policy.updated_by = days, 0 if days is None else 1, current_admin.id
    db.add(policy)
    OperationLogService.write(db, admin_id=current_admin.id, module="retrieval_log", action="retention_update", target_id=None)
    db.commit()
    return api_success({"enabled": bool(policy.enabled), "retentionDays": policy.retention_days})


def _cleanup_before(payload: dict) -> datetime:
    raw = str(payload.get("before") or "").strip()
    if not raw:
        raise HTTPException(status_code=400, detail="必须指定清理日期")
    try:
        return datetime.fromisoformat(raw.replace("Z", "+00:00")).replace(tzinfo=None)
    except ValueError as exc:
        raise HTTPException(status_code=400, detail="清理日期格式错误") from exc


@router.get("/retrieval-log/{log_id}")
def retrieval_detail(
    log_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    log = db.get(KnowledgeRetrievalLog, log_id)
    if log is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="检索日志不存在")
    candidates = db.scalars(
        select(KnowledgeRetrievalCandidate)
        .where(KnowledgeRetrievalCandidate.retrieval_log_id == log.id)
        .order_by(KnowledgeRetrievalCandidate.selected.desc(), KnowledgeRetrievalCandidate.id)
    ).all()
    knowledge_ids = {item.knowledge_id for item in candidates}
    knowledge_map = {
        item.id: item
        for item in db.scalars(select(Knowledge).where(Knowledge.id.in_(knowledge_ids))).all()
    } if knowledge_ids else {}
    data = _retrieval_dict(log, detail=True)
    data["candidates"] = [
        {
            "id": item.id,
            "knowledgeId": item.knowledge_id,
            "knowledgeName": knowledge_map[item.knowledge_id].name if item.knowledge_id in knowledge_map else "",
            "knowledgeType": knowledge_map[item.knowledge_id].knowledge_type if item.knowledge_id in knowledge_map else "general",
            "knowledgeTypeLabel": _knowledge_type_label(knowledge_map[item.knowledge_id].knowledge_type if item.knowledge_id in knowledge_map else "general"),
            "versionId": item.version_id,
            "chunkId": item.chunk_id,
            "sectionId": item.section_id,
            "lexicalScore": item.lexical_score,
            "rerankScore": item.rerank_score,
            "selected": item.selected == 1,
            "discardReason": item.discard_reason,
        }
        for item in candidates
    ]
    return api_success(data)


def _knowledge_type_label(value: str) -> str:
    return {
        "fixed": "固定信息类·最高优先级",
        "course": "课程知识库",
        "qa": "答疑知识库",
        "general": "通用知识库",
    }.get(value, value or "通用知识库")


@router.get("/attention/list")
def attention_list(
    priority: str = Query(default=""),
    statusValue: str = Query(default="", alias="status"),
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=20, ge=10, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    query = select(HumanAttentionRecord)
    if priority:
        query = query.where(HumanAttentionRecord.priority == priority)
    if statusValue:
        query = query.where(HumanAttentionRecord.status == statusValue)
    total = db.scalar(select(func.count()).select_from(query.subquery())) or 0
    rows = db.scalars(query.order_by(HumanAttentionRecord.id.desc()).offset((page - 1) * pageSize).limit(pageSize)).all()
    return api_success(page_result([_attention_dict(item) for item in rows], total=total, page=page, page_size=pageSize))


@router.put("/attention/{attention_id}")
def update_attention(
    attention_id: int,
    payload: AttentionUpdateRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    item = db.get(HumanAttentionRecord, attention_id)
    if item is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="人工关注记录不存在")
    previous = item.status
    item.status = payload.status
    item.handler_id = current_admin.id
    item.handler_note = payload.note
    db.add_all(
        [
            item,
            HumanAttentionHistory(
                attention_id=item.id,
                from_status=previous,
                to_status=payload.status,
                note=payload.note,
                operated_by=current_admin.id,
            ),
        ]
    )
    OperationLogService.write(db, admin_id=current_admin.id, module="attention", action="update", target_id=item.id)
    db.commit()
    return api_success(_attention_dict(item))


@router.delete("/attention/{attention_id}")
def delete_attention(
    attention_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    item = db.get(HumanAttentionRecord, attention_id)
    if item is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="人工关注记录不存在")
    histories = db.scalars(
        select(HumanAttentionHistory).where(HumanAttentionHistory.attention_id == item.id)
    ).all()
    for history in histories:
        db.delete(history)
    db.delete(item)
    OperationLogService.write(db, admin_id=current_admin.id, module="attention", action="delete", target_id=attention_id)
    db.commit()
    return api_success()


def _retrieval_dict(item: KnowledgeRetrievalLog, *, detail: bool) -> dict:
    data = {
        "id": item.id,
        "sessionId": item.session_id,
        "messageId": item.message_id,
        "userId": item.user_id,
        "question": item.question,
        "knowledgeCalled": item.knowledge_called == 1,
        "selectedKnowledgeIds": item.selected_knowledge_ids,
        "queryTerms": _json(item.query_terms),
        "finalSectionIds": item.final_section_ids,
        "status": item.status,
        "toolCostMs": item.tool_cost_ms,
        "totalCostMs": item.total_cost_ms,
        "errorMessage": item.error_message,
        "attentionCreated": item.attention_created == 1,
        "createdAt": item.created_at,
    }
    if detail:
        data.update(
            {
                "catalogSnapshot": _json(item.catalog_snapshot),
                "catalogHash": item.catalog_hash,
                "safetyRuleVersion": item.safety_rule_version,
                "toolTrace": _json(item.tool_trace),
                "discardedConflicts": _json(item.discarded_conflicts),
                "finalAnswer": item.final_answer,
            }
        )
    return data


def _attention_dict(item: HumanAttentionRecord) -> dict:
    return {
        "id": item.id,
        "sessionId": item.session_id,
        "messageId": item.message_id,
        "userId": item.user_id,
        "triggerMessage": item.trigger_message,
        "problemSummary": item.problem_summary,
        "triggerReason": item.trigger_reason,
        "priority": item.priority,
        "status": item.status,
        "handlerId": item.handler_id,
        "handlerNote": item.handler_note,
        "createdAt": item.created_at,
        "updatedAt": item.updated_at,
    }


def _json(raw: str | None):
    if not raw:
        return None
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        return raw
