from __future__ import annotations

import csv
import json
from datetime import datetime
from io import StringIO
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi.responses import Response
from sqlalchemy import exists, func, or_, select
from sqlalchemy.orm import Session, load_only

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.chat import ChatMessage, ChatSession, TopicSession
from app.models.growth import ShareDraft, TeacherHelpCard, TopicSummary
from app.models.logs import AiRequestLog, OperationLog
from app.models.sso import SsoClient
from app.models.user import User
from app.api.pagination import page_result
from app.services.admin_service import OperationLogService
from app.services.question_insight_service import QuestionInsightService
from app.services.growth_profile_service import topic_dict, topic_summary_dict
from app.services.help_card_service import help_card_dict
from app.services.share_draft_service import share_draft_dict

router = APIRouter()


@router.get("/chat/list")
def chat_list(
    keyword: str = Query(default=""),
    userId: int | None = Query(default=None),
    status: str = Query(default=""),
    sourceType: str = Query(default="", pattern="^(|direct|sso)$"),
    sourceClientId: int | None = Query(default=None, gt=0),
    dateFrom: datetime | None = Query(default=None),
    dateTo: datetime | None = Query(default=None),
    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 = _chat_query(
        keyword=keyword,
        user_id=userId,
        status=status,
        source_type=sourceType,
        source_client_id=sourceClientId,
        date_from=dateFrom,
        date_to=dateTo,
    )
    total = db.scalar(select(func.count()).select_from(query.order_by(None).subquery())) or 0
    rows = db.execute(query.order_by(ChatSession.updated_at.desc()).offset((page - 1) * pageSize).limit(pageSize)).all()
    items = [_chat_row_dict(session, user, source_client) for session, user, source_client in rows]
    return api_success(page_result(items, total=total, page=page, page_size=pageSize))


@router.get("/chat/export")
def export_chats(
    keyword: str = Query(default=""),
    userId: int | None = Query(default=None),
    status: str = Query(default=""),
    sourceType: str = Query(default="", pattern="^(|direct|sso)$"),
    sourceClientId: int | None = Query(default=None, gt=0),
    dateFrom: datetime | None = Query(default=None),
    dateTo: datetime | None = Query(default=None),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> Response:
    rows = db.execute(
        _chat_query(
            keyword=keyword,
            user_id=userId,
            status=status,
            source_type=sourceType,
            source_client_id=sourceClientId,
            date_from=dateFrom,
            date_to=dateTo,
        )
        .order_by(ChatSession.updated_at.desc())
        .limit(1000)
    ).all()
    output = StringIO()
    writer = csv.writer(output)
    writer.writerow(["会话ID", "用户ID", "手机号", "姓名", "来源", "标题", "消息数", "最后消息时间", "更新时间"])
    for session, user, source_client in rows:
        writer.writerow(
            [
                session.id,
                session.user_id,
                user.phone if user else "",
                user.name if user else "",
                source_client.name if source_client else "千问千答直接访问",
                session.title,
                session.message_count,
                session.last_message_at,
                session.updated_at,
            ]
        )
    content = "\ufeff" + output.getvalue()
    return Response(
        content=content,
        media_type="text/csv; charset=utf-8",
        headers={"Content-Disposition": 'attachment; filename="chat_records.csv"'},
    )


@router.get("/chat/{session_id}/messages")
def chat_messages(
    session_id: int,
    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:
    exists_session = db.scalar(select(ChatSession.id).where(ChatSession.id == session_id))
    if exists_session is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="会话不存在")
    message_query = select(ChatMessage).where(ChatMessage.session_id == session_id)
    total = db.scalar(select(func.count()).select_from(message_query.subquery())) or 0
    messages = db.scalars(
        message_query
        .order_by(ChatMessage.created_at.asc(), ChatMessage.id.asc())
        .offset((page - 1) * pageSize)
        .limit(pageSize)
    ).all()
    return api_success(page_result([_message_dict(item) for item in messages], total=total, page=page, page_size=pageSize))


@router.get("/chat/{session_id}")
def chat_detail(
    session_id: int,
    messagePage: int = Query(default=1, ge=1),
    messagePageSize: int = Query(default=20, ge=10, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    session_row = db.execute(
        select(ChatSession, User, SsoClient)
        .join(User, User.id == ChatSession.user_id, isouter=True)
        .join(SsoClient, SsoClient.id == ChatSession.source_client_id, isouter=True)
        .where(ChatSession.id == session_id)
    ).first()
    if session_row is None:
        from fastapi import HTTPException, status

        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="会话不存在")

    session, user, source_client = session_row
    message_query = select(ChatMessage).where(ChatMessage.session_id == session_id)
    message_total = db.scalar(select(func.count()).select_from(message_query.subquery())) or 0
    messages = db.scalars(
        message_query
        .order_by(ChatMessage.created_at.asc(), ChatMessage.id.asc())
        .offset((messagePage - 1) * messagePageSize)
        .limit(messagePageSize)
    ).all()
    ai_logs = db.scalars(
        select(AiRequestLog)
        .where(AiRequestLog.session_id == session_id)
        .order_by(AiRequestLog.created_at.asc(), AiRequestLog.id.asc())
    ).all()
    topics = _topic_rows(db, session_id)
    topic_ids = [item["id"] for item in topics]
    help_cards = []
    share_drafts = []
    if topic_ids:
        help_cards = list(
            db.scalars(
                select(TeacherHelpCard)
                .where(TeacherHelpCard.topic_session_id.in_(topic_ids))
                .order_by(TeacherHelpCard.created_at.desc(), TeacherHelpCard.id.desc())
            )
        )
        share_drafts = list(
            db.scalars(
                select(ShareDraft)
                .where(ShareDraft.topic_session_id.in_(topic_ids))
                .order_by(ShareDraft.created_at.desc(), ShareDraft.id.desc())
            )
        )
    return api_success(
        {
            "session": _chat_row_dict(session, user, source_client),
            "messages": [_message_dict(item) for item in messages],
            "messagesPage": page_result(
                [_message_dict(item) for item in messages],
                total=message_total,
                page=messagePage,
                page_size=messagePageSize,
            ),
            "aiLogs": [_ai_log_dict(item, include_prompt=True) for item in ai_logs],
            "topics": topics,
            "helpCards": [help_card_dict(item) for item in help_cards],
            "shareDrafts": [share_draft_dict(item) for item in share_drafts],
        }
    )


@router.get("/log/list")
def operation_logs(
    module: str = Query(default=""),
    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(OperationLog)
    if module:
        query = query.where(OperationLog.module == module)
    total = db.scalar(select(func.count()).select_from(query.subquery())) or 0
    logs = db.scalars(
        query.options(load_only(OperationLog.id, OperationLog.admin_id, OperationLog.module, OperationLog.action, OperationLog.target_id, OperationLog.result, OperationLog.created_at))
        .order_by(OperationLog.created_at.desc()).offset((page - 1) * pageSize).limit(pageSize)
    ).all()
    return api_success(page_result(
        [
            {
                "id": item.id,
                "adminId": item.admin_id,
                "module": item.module,
                "action": item.action,
                "targetId": item.target_id,
                "result": item.result,
                "createdAt": item.created_at,
            }
            for item in logs
        ], total=total, page=page, page_size=pageSize
    ))


@router.get("/ai-log/list")
def ai_logs(
    sessionId: int | None = Query(default=None),
    userId: int | None = Query(default=None),
    status: str = Query(default=""),
    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(AiRequestLog)
    if sessionId is not None:
        query = query.where(AiRequestLog.session_id == sessionId)
    if userId is not None:
        query = query.where(AiRequestLog.user_id == userId)
    if status:
        query = query.where(AiRequestLog.status == status)
    total = db.scalar(select(func.count()).select_from(query.subquery())) or 0
    logs = db.scalars(
        query.options(load_only(
            AiRequestLog.id, AiRequestLog.session_id, AiRequestLog.message_id, AiRequestLog.user_id,
            AiRequestLog.model_name, AiRequestLog.knowledge_ids, AiRequestLog.retrieve_count,
            AiRequestLog.input_token, AiRequestLog.output_token, AiRequestLog.total_token,
            AiRequestLog.cost_ms, AiRequestLog.estimated_cost, AiRequestLog.currency,
            AiRequestLog.route_reason, AiRequestLog.question_type, AiRequestLog.knowledge_hit,
            AiRequestLog.status, AiRequestLog.error_message, AiRequestLog.created_at,
        )).order_by(AiRequestLog.created_at.desc()).offset((page - 1) * pageSize).limit(pageSize)
    ).all()
    return api_success(page_result([_ai_log_dict(item, include_chunks=False) for item in logs], total=total, page=page, page_size=pageSize))


@router.get("/ai-log/{log_id}")
def ai_log_detail(
    log_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    log = db.get(AiRequestLog, log_id)
    if log is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="AI 请求日志不存在")
    return api_success(_ai_log_dict(log, include_prompt=True))


@router.get("/question-insights/summary")
def question_insights(
    dateFrom: datetime | None = Query(default=None),
    dateTo: datetime | None = Query(default=None),
    minCount: int = Query(default=2, ge=1, le=50),
    maxMessages: int = Query(default=5000, ge=100, le=20000),
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=20, ge=10, le=50),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    return api_success(
        QuestionInsightService.summarize(
            db,
            date_from=dateFrom,
            date_to=dateTo,
            min_count=minCount,
            max_messages=maxMessages,
            page=page,
            page_size=pageSize,
        )
    )


@router.post("/question-insights/refresh")
def refresh_question_insights(
    dateFrom: datetime | None = Query(default=None),
    dateTo: datetime | None = Query(default=None),
    maxMessages: int = Query(default=5000, ge=100, le=20000),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    result = QuestionInsightService.refresh(
        db,
        date_from=dateFrom,
        date_to=dateTo,
        max_messages=maxMessages,
    )
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="question_insight",
        action=f"refresh:{result['processedMessages']}",
    )
    db.commit()
    return api_success(result)


def _chat_query(
    *,
    keyword: str,
    user_id: int | None,
    status: str,
    source_type: str,
    source_client_id: int | None,
    date_from: datetime | None,
    date_to: datetime | None,
):
    query = (
        select(ChatSession, User, SsoClient)
        .join(User, User.id == ChatSession.user_id, isouter=True)
        .join(SsoClient, SsoClient.id == ChatSession.source_client_id, isouter=True)
        .where(ChatSession.is_deleted == 0)
    )
    if user_id is not None:
        query = query.where(ChatSession.user_id == user_id)
    if source_type:
        query = query.where(ChatSession.source_type == source_type)
    if source_client_id is not None:
        query = query.where(ChatSession.source_type == "sso", ChatSession.source_client_id == source_client_id)
    if date_from is not None:
        query = query.where(ChatSession.updated_at >= date_from.replace(tzinfo=None))
    if date_to is not None:
        query = query.where(ChatSession.updated_at <= date_to.replace(tzinfo=None))
    if status:
        query = query.where(
            exists()
            .where(ChatMessage.session_id == ChatSession.id)
            .where(ChatMessage.message_status == status)
        )
    if keyword:
        like = f"%{keyword}%"
        query = query.where(
            or_(
                ChatSession.title.like(like),
                User.phone.like(like),
                User.name.like(like),
                exists()
                .where(ChatMessage.session_id == ChatSession.id)
                .where(ChatMessage.content.like(like)),
            )
        )
    return query


def _chat_row_dict(session: ChatSession, user: User | None, source_client: SsoClient | None = None) -> dict:
    return {
        "id": session.id,
        "userId": session.user_id,
        "userPhone": user.phone if user else "",
        "userName": user.name if user else "",
        "sourceType": session.source_type,
        "sourceClientId": session.source_client_id,
        "sourceName": source_client.name if source_client else "千问千答直接访问",
        "title": session.title,
        "messageCount": session.message_count,
        "lastMessageAt": session.last_message_at,
        "updatedAt": session.updated_at,
    }


def _message_dict(message: ChatMessage) -> dict:
    return {
        "id": message.id,
        "sessionId": message.session_id,
        "topicSessionId": message.topic_session_id,
        "userId": message.user_id,
        "role": message.role,
        "content": message.content,
        "messageStatus": message.message_status,
        "tokenInput": message.token_input,
        "tokenOutput": message.token_output,
        "responseTimeMs": message.response_time_ms,
        "modelId": message.model_id,
        "createdAt": message.created_at,
    }


def _topic_rows(db: Session, session_id: int) -> list[dict]:
    rows = db.execute(
        select(TopicSession, TopicSummary)
        .join(TopicSummary, TopicSummary.topic_session_id == TopicSession.id, isouter=True)
        .where(TopicSession.chat_session_id == session_id)
        .order_by(TopicSession.started_at.asc(), TopicSession.id.asc())
    ).all()
    return [
        {
            **topic_dict(topic),
            "summary": topic_summary_dict(summary) if summary else None,
        }
        for topic, summary in rows
    ]


def _ai_log_dict(log: AiRequestLog, *, include_prompt: bool = False, include_chunks: bool = True) -> dict:
    data = {
        "id": log.id,
        "sessionId": log.session_id,
        "messageId": log.message_id,
        "userId": log.user_id,
        "modelName": log.model_name,
        "knowledgeIds": log.knowledge_ids,
        "retrieveCount": log.retrieve_count,
        "retrievedChunks": _parse_retrieved_chunks(log.retrieved_chunks) if include_chunks else [],
        "inputToken": log.input_token,
        "outputToken": log.output_token,
        "totalToken": log.total_token,
        "costMs": log.cost_ms,
        "estimatedCost": float(log.estimated_cost) if log.estimated_cost is not None else None,
        "currency": log.currency,
        "routeReason": log.route_reason,
        "questionType": log.question_type,
        "knowledgeHit": bool(log.knowledge_hit),
        "status": log.status,
        "errorMessage": log.error_message,
        "createdAt": log.created_at,
    }
    if include_prompt:
        data["prompt"] = log.prompt
    return data


def _parse_retrieved_chunks(raw: str | None) -> list[dict[str, Any]]:
    if not raw:
        return []
    try:
        chunks = json.loads(raw)
    except json.JSONDecodeError:
        return []
    if not isinstance(chunks, list):
        return []
    normalized: list[dict[str, Any]] = []
    for index, item in enumerate(chunks, start=1):
        if not isinstance(item, dict):
            continue
        normalized.append(
            {
                "index": item.get("index") or index,
                "knowledgeId": item.get("knowledgeId"),
                "knowledgeName": item.get("knowledgeName") or "",
                "title": item.get("title") or "",
                "content": item.get("content") or "",
                "sourceUrl": item.get("sourceUrl") or None,
            }
        )
    return normalized
