from __future__ import annotations

import json
from collections.abc import AsyncIterator

from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi.encoders import jsonable_encoder
from fastapi.responses import StreamingResponse
from sqlalchemy import func, select
from sqlalchemy.orm import Session

from app.api.pagination import page_result
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.ai_config import ModelConfig, Prompt, SystemConfig
from app.models.knowledge import Knowledge
from app.schemas.admin import (
    AgentDebugRequest,
    AgentRuntimeConfigSaveRequest,
    DefaultModelRequest,
    EnableModelRequest,
    ModelSaveRequest,
    PromptSaveRequest,
    SystemConfigSaveRequest,
)
from app.services.admin_service import OperationLogService
from app.services.agent_debug_service import AgentDebugService
from app.services.feishu_service import FeishuKnowledgeService
from app.services.knowledge_service import KnowledgeScope
from app.services.model_service import ModelClientService
from app.services.reasoning_policy_service import ReasoningPolicyService
from app.services.response_style_service import ResponseStyleService
from app.services.secret_service import MASKED_SECRET, SENSITIVE_CONFIG_KEYS, SecretService

router = APIRouter()

DEFAULT_PROMPT = "你是大本营答疑助手。你只能基于提供的知识片段回答，不能编造。"


@router.get("/prompt")
def get_prompt(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    row = db.execute(
        select(Prompt, Admin)
        .outerjoin(Admin, Admin.id == Prompt.updated_by)
        .order_by(Prompt.updated_at.desc(), Prompt.id.desc())
        .limit(1)
    ).first()
    return api_success(_prompt_detail(row[0], row[1]) if row else _default_prompt_detail())


@router.put("/prompt")
def save_prompt(
    payload: PromptSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    prompt = Prompt(prompt_content=payload.promptContent, change_type="save", updated_by=current_admin.id)
    db.add(prompt)
    db.flush()
    OperationLogService.write(db, admin_id=current_admin.id, module="prompt", action="save", target_id=prompt.id)
    db.commit()
    db.refresh(prompt)
    return api_success(_prompt_detail(prompt, current_admin))


@router.post("/prompt/reset")
def reset_prompt(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    prompt = Prompt(prompt_content=DEFAULT_PROMPT, change_type="reset", updated_by=current_admin.id)
    db.add(prompt)
    db.flush()
    OperationLogService.write(db, admin_id=current_admin.id, module="prompt", action="reset", target_id=prompt.id)
    db.commit()
    db.refresh(prompt)
    return api_success(_prompt_detail(prompt, current_admin))


@router.get("/prompt/history")
def prompt_history(
    page: int = Query(default=1, ge=1),
    pageSize: int = Query(default=10, ge=10, le=100),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    total = db.scalar(select(func.count(Prompt.id))) or 0
    rows = db.execute(
        select(Prompt, Admin)
        .outerjoin(Admin, Admin.id == Prompt.updated_by)
        .order_by(Prompt.updated_at.desc(), Prompt.id.desc())
        .offset((page - 1) * pageSize)
        .limit(pageSize)
    ).all()
    items = [
        _prompt_summary(prompt, admin, is_current=index == 0 and page == 1)
        for index, (prompt, admin) in enumerate(rows)
    ]
    return api_success(page_result(items, total=total, page=page, page_size=pageSize))


@router.get("/prompt/history/{prompt_id}")
def prompt_history_detail(
    prompt_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    row = db.execute(
        select(Prompt, Admin)
        .outerjoin(Admin, Admin.id == Prompt.updated_by)
        .where(Prompt.id == prompt_id)
    ).first()
    if row is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="提示词版本不存在")
    return api_success(_prompt_detail(row[0], row[1]))


@router.post("/prompt/history/{prompt_id}/restore")
def restore_prompt(
    prompt_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    source = db.get(Prompt, prompt_id)
    if source is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="提示词版本不存在")
    restored = Prompt(
        prompt_content=source.prompt_content,
        change_type="restore",
        source_prompt_id=source.id,
        updated_by=current_admin.id,
    )
    db.add(restored)
    db.flush()
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="prompt",
        action="restore",
        target_id=restored.id,
    )
    db.commit()
    db.refresh(restored)
    return api_success(_prompt_detail(restored, current_admin))


@router.post("/agent/debug")
async def debug_agent(
    payload: AgentDebugRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = db.get(ModelConfig, payload.modelId)
    if model is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")

    rag_result = await AgentDebugService.build_result(db, payload)
    result = await ModelClientService.debug_model_async(
        model,
        rag_result,
        AgentDebugService.overrides(payload),
    )
    result["retrievalTrace"] = rag_result.tool_trace or []
    result["retrievalLogId"] = rag_result.retrieval_log_id
    OperationLogService.write(db, admin_id=current_admin.id, module="agent", action="debug", target_id=model.id)
    db.commit()
    return api_success(result)


@router.post("/agent/debug/stream")
def debug_agent_stream(
    payload: AgentDebugRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> StreamingResponse:
    return StreamingResponse(
        _debug_agent_stream(payload, db, current_admin),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache, no-transform",
            "Connection": "keep-alive",
            "X-Accel-Buffering": "no",
        },
    )


async def _debug_agent_stream(
    payload: AgentDebugRequest,
    db: Session,
    current_admin: Admin,
) -> AsyncIterator[str]:
    async for event in AgentDebugService.stream(payload, db, current_admin):
        yield _admin_sse_event(event)
    yield _admin_sse_done()


def _admin_sse_event(event: dict) -> str:
    event = jsonable_encoder(event)
    return f"data: {json.dumps(event, ensure_ascii=False)}\n\n"


def _admin_sse_done() -> str:
    return "data: [DONE]\n\n"


@router.get("/agent/runtime-config")
def get_agent_runtime_config(
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = _enabled_model(db)
    return api_success(_agent_runtime_config_dict(model, ReasoningPolicyService.is_visible(db), ResponseStyleService.get_depth(db)))


@router.put("/agent/runtime-config")
def save_agent_runtime_config(
    payload: AgentRuntimeConfigSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = _enabled_model(db)
    if model is None:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="当前没有已启用模型，请先在模型管理中启用一个模型",
        )
    model.temperature = payload.temperature
    model.top_p = payload.topP
    model.top_k = payload.topK
    model.presence_penalty = payload.presencePenalty
    model.frequency_penalty = payload.frequencyPenalty
    model.max_token = payload.maxToken
    model.stream_enabled = payload.streamEnabled
    db.add(model)
    ReasoningPolicyService.set_visible(db, payload.reasoningVisible == 1, current_admin.id)
    ResponseStyleService.set_depth(db, payload.responseDepth, current_admin.id)
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="agent",
        action="save_runtime_config",
        target_id=model.id,
    )
    db.commit()
    db.refresh(model)
    return api_success(_agent_runtime_config_dict(model, ReasoningPolicyService.is_visible(db), ResponseStyleService.get_depth(db)))


@router.get("/model/list")
def list_models(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    models = db.scalars(
        select(ModelConfig).order_by(
            ModelConfig.is_default.desc(),
            ModelConfig.enabled.desc(),
            ModelConfig.id.desc(),
        )
    ).all()
    return api_success([_model_dict(model) for model in models])


@router.post("/model")
def create_model(
    payload: ModelSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    if not payload.apiKey:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="API Key 不能为空")
    model = ModelConfig(
        provider=payload.provider,
        display_name=payload.displayName,
        api_type=payload.apiType,
        model_name=payload.modelName,
        base_url=payload.baseUrl,
        api_url=payload.apiUrl,
        api_key=SecretService.encrypt(payload.apiKey),
        auth_type=payload.authType,
        api_version=payload.apiVersion,
        temperature=payload.temperature,
        top_p=payload.topP,
        top_k=payload.topK,
        presence_penalty=payload.presencePenalty,
        frequency_penalty=payload.frequencyPenalty,
        max_token=payload.maxToken,
        context_window=payload.contextWindow,
        stream_enabled=payload.streamEnabled,
        response_format=payload.responseFormat,
        extra_params=payload.extraParams,
        remark=payload.remark,
        timeout_second=payload.timeoutSecond,
        input_price_per_1k=payload.inputPricePer1k,
        output_price_per_1k=payload.outputPricePer1k,
        currency=payload.currency or "CNY",
        usage_scenarios=payload.usageScenarios,
        allow_summary=payload.allowSummary,
        allow_report=payload.allowReport,
        allow_fixed_info=payload.allowFixedInfo,
        allow_simple_knowledge=payload.allowSimpleKnowledge,
        allow_deep_chat=payload.allowDeepChat,
        enabled=0,
        is_default=0,
    )
    db.add(model)
    db.flush()
    OperationLogService.write(db, admin_id=current_admin.id, module="model", action="create", target_id=model.id)
    db.commit()
    db.refresh(model)
    return api_success(_model_dict(model))


@router.put("/model/{model_id}")
def update_model(
    model_id: int,
    payload: ModelSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = db.get(ModelConfig, model_id)
    if model is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")

    model.provider = payload.provider
    model.display_name = payload.displayName
    model.api_type = payload.apiType
    model.model_name = payload.modelName
    model.base_url = payload.baseUrl
    model.api_url = payload.apiUrl
    if payload.apiKey and payload.apiKey != MASKED_SECRET:
        model.api_key = SecretService.encrypt(payload.apiKey)
    model.auth_type = payload.authType
    model.api_version = payload.apiVersion
    model.temperature = payload.temperature
    model.top_p = payload.topP
    model.top_k = payload.topK
    model.presence_penalty = payload.presencePenalty
    model.frequency_penalty = payload.frequencyPenalty
    model.max_token = payload.maxToken
    model.context_window = payload.contextWindow
    model.stream_enabled = payload.streamEnabled
    model.response_format = payload.responseFormat
    model.extra_params = payload.extraParams
    model.remark = payload.remark
    model.timeout_second = payload.timeoutSecond
    model.input_price_per_1k = payload.inputPricePer1k
    model.output_price_per_1k = payload.outputPricePer1k
    model.currency = payload.currency or "CNY"
    model.usage_scenarios = payload.usageScenarios
    model.allow_summary = payload.allowSummary
    model.allow_report = payload.allowReport
    model.allow_fixed_info = payload.allowFixedInfo
    model.allow_simple_knowledge = payload.allowSimpleKnowledge
    model.allow_deep_chat = payload.allowDeepChat
    db.add(model)
    OperationLogService.write(db, admin_id=current_admin.id, module="model", action="update", target_id=model.id)
    db.commit()
    db.refresh(model)
    return api_success(_model_dict(model))


@router.post("/model/enable")
def enable_model(
    payload: EnableModelRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    target = db.get(ModelConfig, payload.modelId)
    if target is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")
    if payload.enabled == 0 and target.is_default == 1:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="默认主模型不能直接停用，请先设置另一个默认主模型",
        )
    target.enabled = payload.enabled
    if payload.enabled == 1 and _explicit_default_model(db) is None:
        target.is_default = 1
    db.add(target)
    action = "enable" if payload.enabled == 1 else "disable"
    OperationLogService.write(db, admin_id=current_admin.id, module="model", action=action, target_id=target.id)
    db.commit()
    return api_success()


@router.post("/model/default")
def set_default_model(
    payload: DefaultModelRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    target = db.get(ModelConfig, payload.modelId)
    if target is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")
    for model in db.scalars(select(ModelConfig)).all():
        model.is_default = 1 if model.id == target.id else 0
        if model.id == target.id:
            model.enabled = 1
        db.add(model)
    OperationLogService.write(
        db,
        admin_id=current_admin.id,
        module="model",
        action="set_default",
        target_id=target.id,
    )
    db.commit()
    return api_success()


@router.delete("/model/{model_id}")
def delete_model(
    model_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = db.get(ModelConfig, model_id)
    if model is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")
    if model.is_default == 1:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="不能删除默认主模型，请先设置另一个默认主模型",
        )
    db.delete(model)
    OperationLogService.write(db, admin_id=current_admin.id, module="model", action="delete", target_id=model.id)
    db.commit()
    return api_success()


@router.post("/model/{model_id}/test")
def test_model(
    model_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    model = db.get(ModelConfig, model_id)
    if model is None:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="模型不存在")
    result = ModelClientService.test_model(model)
    OperationLogService.write(db, admin_id=current_admin.id, module="model", action="test", target_id=model.id)
    db.commit()
    return api_success(result)


@router.get("/config")
def list_config(db: Session = Depends(get_db), current_admin: Admin = Depends(get_current_admin)) -> dict:
    configs = db.scalars(select(SystemConfig).order_by(SystemConfig.config_key.asc())).all()
    return api_success([_config_dict(config) for config in configs])


@router.put("/config")
def save_config(
    payload: SystemConfigSaveRequest,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin),
) -> dict:
    config = db.scalar(select(SystemConfig).where(SystemConfig.config_key == payload.configKey))
    if config is None:
        config = SystemConfig(config_key=payload.configKey, config_value=payload.configValue)
    if payload.configKey in SENSITIVE_CONFIG_KEYS:
        if payload.configValue == MASKED_SECRET:
            return api_success(_config_dict(config))
        config.config_value = SecretService.encrypt(payload.configValue)
    else:
        config.config_value = payload.configValue
    config.description = payload.description
    config.updated_by = current_admin.id
    db.add(config)
    db.flush()
    OperationLogService.write(db, admin_id=current_admin.id, module="config", action="save", target_id=config.id)
    db.commit()
    db.refresh(config)
    return api_success(_config_dict(config))


def _model_dict(model: ModelConfig) -> dict:
    return {
        "id": model.id,
        "provider": model.provider,
        "displayName": model.display_name,
        "apiType": model.api_type,
        "modelName": model.model_name,
        "baseUrl": model.base_url,
        "apiUrl": model.api_url,
        "apiKeyMasked": SecretService.masked(model.api_key),
        "authType": model.auth_type,
        "apiVersion": model.api_version,
        "temperature": float(model.temperature) if model.temperature is not None else None,
        "topP": float(model.top_p) if model.top_p is not None else None,
        "topK": model.top_k,
        "presencePenalty": float(model.presence_penalty) if model.presence_penalty is not None else None,
        "frequencyPenalty": float(model.frequency_penalty) if model.frequency_penalty is not None else None,
        "maxToken": model.max_token,
        "contextWindow": model.context_window,
        "streamEnabled": model.stream_enabled,
        "responseFormat": model.response_format,
        "extraParams": model.extra_params,
        "remark": model.remark,
        "timeoutSecond": model.timeout_second,
        "enabled": model.enabled,
        "isDefault": model.is_default,
        "inputPricePer1k": float(model.input_price_per_1k) if model.input_price_per_1k is not None else None,
        "outputPricePer1k": float(model.output_price_per_1k) if model.output_price_per_1k is not None else None,
        "currency": model.currency,
        "usageScenarios": model.usage_scenarios,
        "allowSummary": model.allow_summary,
        "allowReport": model.allow_report,
        "allowFixedInfo": model.allow_fixed_info,
        "allowSimpleKnowledge": model.allow_simple_knowledge,
        "allowDeepChat": model.allow_deep_chat,
    }


def _enabled_model(db: Session) -> ModelConfig | None:
    return ModelClientService._get_enabled_model(db)


def _explicit_default_model(db: Session) -> ModelConfig | None:
    return db.scalar(
        select(ModelConfig)
        .where(ModelConfig.enabled == 1, ModelConfig.is_default == 1)
        .order_by(ModelConfig.id.desc())
        .limit(1)
    )


def _agent_runtime_config_dict(model: ModelConfig | None, reasoning_visible: bool = False, response_depth: int = 35) -> dict:
    return {
        "modelId": model.id if model is not None else None,
        "modelName": (model.display_name or model.model_name) if model is not None else None,
        "temperature": float(model.temperature) if model is not None and model.temperature is not None else None,
        "topP": float(model.top_p) if model is not None and model.top_p is not None else None,
        "topK": model.top_k if model is not None else None,
        "presencePenalty": float(model.presence_penalty) if model is not None and model.presence_penalty is not None else None,
        "frequencyPenalty": float(model.frequency_penalty) if model is not None and model.frequency_penalty is not None else None,
        "maxToken": model.max_token if model is not None and model.max_token is not None else 8192,
        "streamEnabled": model.stream_enabled if model is not None else 1,
        "reasoningVisible": 1 if reasoning_visible else 0,
        "responseDepth": response_depth,
    }


def _default_prompt_detail() -> dict:
    return {
        "id": None,
        "promptContent": DEFAULT_PROMPT,
        "changeType": "default",
        "sourcePromptId": None,
        "updatedByName": "系统默认",
        "updatedAt": None,
        "charCount": len(DEFAULT_PROMPT),
    }


def _prompt_summary(prompt: Prompt, admin: Admin | None, *, is_current: bool) -> dict:
    compact = " ".join(prompt.prompt_content.split())
    return {
        "id": prompt.id,
        "preview": compact[:140],
        "charCount": len(prompt.prompt_content),
        "changeType": prompt.change_type,
        "sourcePromptId": prompt.source_prompt_id,
        "updatedByName": admin.name if admin else "未知管理员",
        "updatedAt": prompt.updated_at,
        "isCurrent": is_current,
    }


def _prompt_detail(prompt: Prompt, admin: Admin | None = None) -> dict:
    return {
        "id": prompt.id,
        "promptContent": prompt.prompt_content,
        "changeType": prompt.change_type,
        "sourcePromptId": prompt.source_prompt_id,
        "updatedByName": admin.name if admin else None,
        "updatedAt": prompt.updated_at,
        "charCount": len(prompt.prompt_content),
    }


def _agent_knowledge_scopes(db: Session, knowledge_ids: list[int]) -> list[KnowledgeScope]:
    query = select(Knowledge).where(Knowledge.status == 1)
    if knowledge_ids:
        query = query.where(Knowledge.id.in_(knowledge_ids))
    rows = db.scalars(query.order_by(Knowledge.id.asc())).all()
    return [
        KnowledgeScope(
            id=item.id,
            name=item.name,
            feishu_space_id=item.feishu_space_id,
            feishu_node_id=item.feishu_node_id,
        )
        for item in rows
    ]


def _build_agent_debug_prompt(prompt_content: str, question: str, chunks: list) -> str:
    context = "\n\n".join(
        f"[知识片段 {index}] 来源：{chunk.knowledge_name} / {chunk.title}\n{chunk.content}"
        for index, chunk in enumerate(chunks, start=1)
    )
    if not context:
        context = "未检索到相关知识片段。"
    return f"{prompt_content.strip()}\n\n{context}\n\n用户问题：{question.strip()}"


@router.post("/feishu/cache/clear")
def clear_feishu_cache(current_admin: Admin = Depends(get_current_admin)) -> dict:
    count = FeishuKnowledgeService.clear_cache()
    return api_success({"cleared": count, "message": f"已清空 {count} 条飞书缓存"})


def _config_dict(config: SystemConfig) -> dict:
    value = config.config_value
    if config.config_key in SENSITIVE_CONFIG_KEYS:
        value = SecretService.masked(value)
    return {
        "id": config.id,
        "configKey": config.config_key,
        "configValue": value,
        "description": config.description,
        "updatedAt": config.updated_at,
    }
