"""LLM configuration management API endpoints."""

from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from langchain_openai import ChatOpenAI
from langchain_core.messages import AIMessage
from sqlalchemy.orm import Session
from sqlalchemy import or_, select, delete, update

from loguru import logger

from th_agenter.llm.embed.embed_llm import BGEEmbedLLM, EmbedLLM
from th_agenter.llm.online.online_llm import OnlineLLM
from ...db.database import get_session
from ...models.user import User
from ...models.llm_config import LLMConfig
from th_agenter.llm.base_llm import LLMConfig_DataClass
from ...core.simple_permissions import require_super_admin, require_authenticated_user
from ...schemas.llm_config import (
    LLMConfigCreate, LLMConfigUpdate, LLMConfigResponse,
    LLMConfigTest
)
from th_agenter.services.document_processor import get_document_processor
from utils.util_exceptions import HxfResponse

router = APIRouter(prefix="/llm-configs", tags=["llm-configs"])

@router.get("/", response_model=List[LLMConfigResponse], summary="获取大模型配置列表")
async def get_llm_configs(
    skip: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=1000),
    search: Optional[str] = Query(None),
    provider: Optional[str] = Query(None),
    is_active: Optional[bool] = Query(None),
    is_embedding: Optional[bool] = Query(None),
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """获取大模型配置列表."""
    session.title = "获取大模型配置列表"
    session.desc = f"START: 获取大模型配置列表, skip={skip}, limit={limit}, search={search}, provider={provider}, is_active={is_active}, is_embedding={is_embedding}"
    stmt = select(LLMConfig)
    
    # 搜索
    if search:
        stmt = stmt.where(
            or_(
                LLMConfig.name.ilike(f"%{search}%"),
                LLMConfig.model_name.ilike(f"%{search}%"),
                LLMConfig.description.ilike(f"%{search}%")
            )
        )
    
    # 服务商筛选
    if provider:
        stmt = stmt.where(LLMConfig.provider == provider)
    
    # 状态筛选
    if is_active is not None:
        stmt = stmt.where(LLMConfig.is_active == is_active)
    
    # 模型类型筛选
    if is_embedding is not None:
        stmt = stmt.where(LLMConfig.is_embedding == is_embedding)
    
    # 排序
    stmt = stmt.order_by(LLMConfig.name)
    
    # 分页
    stmt = stmt.offset(skip).limit(limit)
    configs = (await session.execute(stmt)).scalars().all()
    session.desc = f"SUCCESS: 获取 {len(configs)} 个大模型配置 ..."
    return HxfResponse([config.to_dict(include_sensitive=True) for config in configs])


@router.get("/providers", summary="获取支持的大模型服务商列表")
async def get_llm_providers(
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """获取支持的大模型服务商列表."""
    session.desc = "START: 获取支持的大模型服务商列表"
    stmt = select(LLMConfig.provider).distinct()
    providers = (await session.execute(stmt)).scalars().all()
    session.desc = f"SUCCESS: 获取 {len(providers)} 个大模型服务商"
    return HxfResponse([provider for provider in providers if provider])



@router.get("/active", response_model=List[LLMConfigResponse], summary="获取所有激活的大模型配置")
async def get_active_llm_configs(
    is_embedding: Optional[bool] = Query(None),
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """获取所有激活的大模型配置."""
    session.desc = f"START: 获取所有激活的大模型配置, is_embedding={is_embedding}"
    stmt = select(LLMConfig).where(LLMConfig.is_active == True)
    
    if is_embedding is not None:
        stmt = stmt.where(LLMConfig.is_embedding == is_embedding)
    
    stmt = stmt.order_by(LLMConfig.created_at)
    configs = (await session.execute(stmt)).scalars().all()
    session.desc = f"SUCCESS: 获取 {len(configs)} 个激活的大模型配置"    
    return HxfResponse([config.to_dict(include_sensitive=True) for config in configs])

@router.get("/default", response_model=LLMConfigResponse, summary="获取默认大模型配置")
async def get_default_llm_config(
    is_embedding: bool = Query(False, description="是否获取嵌入模型默认配置"),
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """获取默认大模型配置."""
    session.desc = f"START: 获取默认大模型配置, is_embedding={is_embedding}"
    stmt = select(LLMConfig).where(
        LLMConfig.is_default == True,
        LLMConfig.is_embedding == is_embedding,
        LLMConfig.is_active == True
    )
    config = (await session.execute(stmt)).scalar_one_or_none()
    
    if not config:
        model_type = "嵌入模型" if is_embedding else "对话模型"
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"未找到默认{model_type}配置"
        )
    
    session.desc = f"SUCCESS: 获取默认大模型配置, is_embedding={is_embedding}"
    return HxfResponse(config.to_dict(include_sensitive=True))

@router.get("/{config_id}", response_model=LLMConfigResponse, summary="获取大模型配置详情")
async def get_llm_config(
    config_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """获取大模型配置详情."""
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
        
    return HxfResponse(config.to_dict(include_sensitive=True))


@router.post("/", response_model=LLMConfigResponse, status_code=status.HTTP_201_CREATED, summary="创建大模型配置")
async def create_llm_config(
    config_data: LLMConfigCreate,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_super_admin)
):
    """创建大模型配置."""
    # 检查配置名称是否已存在
    # 先保存当前用户名，避免在refresh后访问可能导致MissingGreenlet错误
    username = current_user.username
    session.desc = f"START: 创建大模型配置, name={config_data.name}"
    stmt = select(LLMConfig).where(LLMConfig.name == config_data.name)
    existing_config = (await session.execute(stmt)).scalar_one_or_none()
    if existing_config:
        session.desc = f"ERROR: 配置名称已存在, name={config_data.name}"
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="配置名称已存在"
        )
    
    # 创建配置对象
    config = LLMConfig_DataClass(
        name=config_data.name,
        provider=config_data.provider,
        model_name=config_data.model_name,
        api_key=config_data.api_key,
        base_url=config_data.base_url,
        max_tokens=config_data.max_tokens,
        temperature=config_data.temperature,
        top_p=config_data.top_p,
        frequency_penalty=config_data.frequency_penalty,
        presence_penalty=config_data.presence_penalty,
        description=config_data.description,
        is_active=config_data.is_active,
        is_default=config_data.is_default,
        is_embedding=config_data.is_embedding,
        extra_config=config_data.extra_config or {}
    )
    
    # 验证配置
    validation_result = config.validate_config()
    if not validation_result['valid']:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=validation_result['error']
        )
    
    # 如果设为默认，取消同类型的其他默认配置
    if config_data.is_default:
        stmt = update(LLMConfig).where(
            LLMConfig.is_embedding == config_data.is_embedding
        ).values({"is_default": False})
        await session.execute(stmt)
    
    session.desc = f"验证大模型配置, config_data"
    # 创建配置
    config = LLMConfig_DataClass(
        name=config_data.name,
        provider=config_data.provider,
        model_name=config_data.model_name,
        api_key=config_data.api_key,
        base_url=config_data.base_url,
        max_tokens=config_data.max_tokens,
        temperature=config_data.temperature,
        top_p=config_data.top_p,
        frequency_penalty=config_data.frequency_penalty,
        presence_penalty=config_data.presence_penalty,
        description=config_data.description,
        is_active=config_data.is_active,
        is_default=config_data.is_default,
        is_embedding=config_data.is_embedding,
        extra_config=config_data.extra_config or {}
    )
    # Audit fields are set automatically by SQLAlchemy event listener
    
    session.add(config)
    await session.commit()
    await session.refresh(config)    
    session.desc = f"SUCCESS: 创建大模型配置, name={config.name}  by user {username}"
    return HxfResponse(config.to_dict())
    

@router.put("/{config_id}", response_model=LLMConfigResponse, summary="更新大模型配置")
async def update_llm_config(
    config_id: int,
    config_data: LLMConfigUpdate,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """更新大模型配置."""
    username = current_user.username
    session.desc = f"START: 更新大模型配置, id={config_id}"
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
    
    # 检查配置名称是否已存在（排除自己）
    if config_data.name and config_data.name != config.name:
        stmt = select(LLMConfig).where(
            LLMConfig.name == config_data.name,
            LLMConfig.id != config_id
        )
        existing_config = (await session.execute(stmt)).scalar_one_or_none()
        if existing_config:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="配置名称已存在"
            )
    
    # 如果设为默认，取消同类型的其他默认配置
    if config_data.is_default is True:
        # 获取当前配置的embedding类型，如果更新中包含is_embedding则使用新值
        is_embedding = config_data.is_embedding if config_data.is_embedding is not None else config.is_embedding
        stmt = update(LLMConfig).where(
            LLMConfig.is_embedding == is_embedding,
            LLMConfig.id != config_id
        ).values({"is_default": False})
        await session.execute(stmt)
    
    # 更新字段
    update_data = config_data.model_dump(exclude_unset=True)
    was_embedding = config.is_embedding
    for field, value in update_data.items():
        setattr(config, field, value)
    
    await session.commit()
    await session.refresh(config)
    
    # 如果设置为默认嵌入模型，更新文档处理器的嵌入模型
    if config.is_embedding and config.is_default:
        try:
            document_processor = await get_document_processor(session)
            await document_processor._init_embeddings(session)
        except Exception as e:
            logger.warning(f"更新文档处理器嵌入模型失败: {str(e)}")
            # 不阻止更新配置，只记录警告
    
    session.desc = f"SUCCESS: 更新大模型配置, id={config_id} by user {username}"
    return HxfResponse(config.to_dict())

@router.delete("/{config_id}", status_code=status.HTTP_204_NO_CONTENT, summary="删除大模型配置")
async def delete_llm_config(
    config_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_super_admin)
):
    """删除大模型配置."""
    username = current_user.username
    session.desc = f"START: 删除大模型配置, id={config_id}"
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
    session.desc = f"待删除大模型记录 {config.to_dict()}"
    # TODO: 检查是否有对话或其他功能正在使用该配置
    # 这里可以添加相关的检查逻辑
    
    # 删除配置
    await session.delete(config)
    await session.commit()
    
    session.desc = f"SUCCESS: 删除大模型配置成功, id={config_id} by user {username}"
    return HxfResponse({"message": "LLM config deleted successfully"})

@router.post("/{config_id}/test", summary="测试连接大模型配置")
async def test_llm_config(
    config_id: int,
    test_data: LLMConfigTest,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """测试连接大模型配置."""
    username = current_user.username
    session.desc = f"TEST: 测试连接大模型配置 {config_id} by user {username}"
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
    
    logger.info(f"TEST: 测试连接大模型配置 {config_id} by user {username}")
    config_name = config.name
    # 验证配置
    validation_result = config.validate_config()
    logger.info(f"TEST: 验证大模型配置 {config_name} validation_result = {validation_result}")
    if not validation_result["valid"]:
        return {
            "success": False,
            "message": f"配置验证失败: {validation_result['error']}",
            "details": validation_result
        }
    
    session.desc = f"准备测试LLM功能  >  测试连接大模型配置 {config.to_dict()}"
    # 尝试创建客户端并发送测试请求
    try:
    #     # 这里应该根据不同的服务商创建相应的客户端
    #     # 由于具体的客户端实现可能因服务商而异，这里提供一个通用的框架
        
        test_message = test_data.message or "Hello, this is a test message."
        session.desc = f"准备测试LLM功能  >  test_message = {test_message}"
        
        # 处理 base_url：如果包含 /chat/completions，需要移除
        original_base_url = config.base_url
        if config.base_url and '/chat/completions' in config.base_url:
            # 移除 /chat/completions 后缀，ChatOpenAI 会自动添加
            config.base_url = config.base_url.replace('/chat/completions', '').rstrip('/')
            logger.info(f"调整 base_url: {original_base_url} -> {config.base_url}")
        
        # 处理 SiliconFlow 的模型名称格式
        original_model_name = config.model_name
        if 'siliconflow' in config.base_url.lower() and '/' not in config.model_name:
            # SiliconFlow 需要 org/model 格式，尝试自动转换
            model_name_lower = config.model_name.lower()
            if 'deepseek' in model_name_lower or 'r1' in model_name_lower:
                # 尝试常见的 DeepSeek 模型格式
                if 'r1' in model_name_lower:
                    config.model_name = 'deepseek-ai/DeepSeek-R1'
                elif 'v3' in model_name_lower:
                    config.model_name = 'deepseek-ai/DeepSeek-V3'
                else:
                    config.model_name = f'deepseek-ai/{config.model_name}'
                logger.info(f"调整 SiliconFlow 模型名称: {original_model_name} -> {config.model_name}")
        
        if config.is_embedding:
            config.provider = "ollama"
            streaming_llm = BGEEmbedLLM(config)
        else:
            streaming_llm = OnlineLLM(config)
        session.desc = f"创建{'EmbeddingLLM' if config.is_embedding else 'OnlineLLM'}完毕  >  测试连接大模型配置 {config.to_dict()}"
        streaming_llm.load_model()  # 加载模型
        session.desc = f"加载模型完毕，模型名称：{config.model_name}，base_url: {config.base_url}，准备测试对话..."

        if config.is_embedding:
            # 测试嵌入模型，使用嵌入API而非聊天API
            test_text = test_message or "Hello, this is a test message for embedding"
            response = streaming_llm.embed_query(test_text)
        else:
            # 测试聊天模型
            from langchain_core.messages import SystemMessage, HumanMessage
            messages = [
                SystemMessage(content="你是一个简洁的助手，回答控制在50字以内"),
                HumanMessage(content=test_message)
            ]
            # 使用异步调用
            import asyncio
            if hasattr(streaming_llm.model, 'ainvoke'):
                response = await streaming_llm.model.ainvoke(messages)
            else:
                # 如果模型不支持异步，使用同步调用但在线程中执行
                response = await asyncio.to_thread(streaming_llm.model.invoke, messages)
        session.desc = f"测试连接大模型配置 {config_name} 成功 >>> 响应: {type(response)}"
        
        return HxfResponse({
            "success": True,
            "message": "LLM测试成功",
            "request": test_message,
            "response": response.content if hasattr(response, 'content') else response,  # 使用转换后的字典
            "latency_ms": 150,  # 模拟延迟
            "config_info": config.to_dict()
        })
        
    except Exception as test_error:
        import traceback
        error_detail = str(test_error)
        error_traceback = traceback.format_exc()
        logger.error(f"测试LLM配置失败: {error_detail}\n{error_traceback}")
        session.desc = f"ERROR: 测试连接大模型配置 {config.name} 失败, error: {error_detail}"
        
        # 提取更详细的错误信息
        if hasattr(test_error, 'response') and hasattr(test_error.response, 'status_code'):
            error_detail = f"HTTP {test_error.response.status_code}: {error_detail}"
            # 如果是模型不存在的错误，提供更详细的提示
            if "Model does not exist" in error_detail or "model" in error_detail.lower():
                original_model = original_model_name if 'original_model_name' in locals() else (config.model_name if 'config' in locals() else "N/A")
                if 'siliconflow' in (original_base_url if 'original_base_url' in locals() else (config.base_url if 'config' in locals() else "")).lower():
                    error_detail += f"\n提示：SiliconFlow 的模型名称格式应为 'org/model-name'（如 'deepseek-ai/DeepSeek-R1'），当前配置: {original_model}"
        elif "Not Found" in error_detail or "404" in error_detail:
            error_detail = f"API端点未找到。请检查 base_url 配置是否正确。当前 base_url: {original_base_url if 'original_base_url' in locals() else config.base_url}"
        
        return HxfResponse({
            "success": False,
            "message": f"LLM测试失败: {error_detail}",
            "test_message": test_message if 'test_message' in locals() else "N/A",
            "config_info": config.to_dict() if 'config' in locals() else {}
        })

@router.post("/{config_id}/toggle-status", summary="切换大模型配置状态")
async def toggle_llm_config_status(
    config_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_super_admin)
):
    """切换大模型配置状态."""
    username = current_user.username
    session.desc = f"START: 切换大模型配置状态, id={config_id} by user {username}"
    
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
    
    # 切换状态
    config.is_active = not config.is_active
    # Audit fields are set automatically by SQLAlchemy event listener
    
    await session.commit()
    await session.refresh(config)
    
    status_text = "激活" if config.is_active else "禁用"
    session.desc = f"SUCCESS: 切换大模型配置状态: {config.name} {status_text} by user {username}"
    
    return HxfResponse({
        "message": f"配置已{status_text}",
        "is_active": config.is_active
    })


@router.post("/{config_id}/set-default", summary="设置默认大模型配置")
async def set_default_llm_config(
    config_id: int,
    session: Session = Depends(get_session),
    current_user: User = Depends(require_authenticated_user)
):
    """设置默认大模型配置."""
    username = current_user.username
    session.desc = f"START: 设置大模型配置 {config_id} 为默认 by user {username}"
    
    stmt = select(LLMConfig).where(LLMConfig.id == config_id)
    config = (await session.execute(stmt)).scalar_one_or_none()
    if not config:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="大模型配置不存在"
        )
    
    # 检查配置是否激活
    if not config.is_active:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="只能将激活的配置设为默认"
        )
    
    # 取消同类型的其他默认配置
    stmt = update(LLMConfig).where(
        LLMConfig.is_embedding == config.is_embedding,
        LLMConfig.id != config_id
    ).values({"is_default": False})
    await session.execute(stmt)
    
    # 设置当前配置为默认
    config.is_default = True
    config.set_audit_fields(current_user.id, is_update=True)
    
    await session.commit()
    await session.refresh(config)
    
    model_type = "嵌入模型" if config.is_embedding else "对话模型"
    # 如果设置为默认嵌入模型，更新文档处理器的嵌入模型
    if config.is_embedding:
        try:
            document_processor = await get_document_processor(session)
            await document_processor._init_embeddings(session)
        except Exception as e:
            logger.warning(f"更新文档处理器嵌入模型失败: {str(e)}")
            # 不阻止设置默认配置，只记录警告
    session.desc = f"SUCCESS: 设置大模型配置 {config.name} ({model_type}) 为默认 by user {username}"
    return HxfResponse({
        "message": f"已将 {config.name} 设为默认{model_type}配置",
        "is_default": config.is_default
    })