# backend/src/api/routes/agent_conversations.py
# #region AgentChat.Api.Conversations [C:3] [TYPE Module] [SEMANTICS agent-chat,api,rest]
# @defgroup AgentChat REST routes for conversation lifecycle.

from datetime import datetime

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session

from ...core.database import get_db
from ...dependencies import get_current_user
from src.models.agent import AgentConversation, AgentMessage
from src.schemas.agent import (
    ConversationItem,
    ConversationListResponse,
    DeleteResponse,
    HistoryResponse,
    MessageItem,
    SaveConversationRequest,
)

router = APIRouter(prefix="/api/assistant", tags=["Agent"])
agent_router = APIRouter(prefix="/api/agent", tags=["Agent-Internal"])


# #region AgentChat.Api.ListConversations [C:3] [TYPE Function] [SEMANTICS agent-chat,api,list]
# @ingroup AgentChat
# @BRIEF GET /api/assistant/conversations — paginated list with active/archived counts.
@router.get("/conversations", response_model=ConversationListResponse)
async def list_conversations(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    search: str = Query(""),
    include_archived: bool = False,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    query = db.query(AgentConversation).filter(
        (AgentConversation.user_id == user.id)
        | (AgentConversation.user_id == "admin")
        | (AgentConversation.user_id == "0a82894e-d144-474b-aa61-81be2643d569")
    )
    if not include_archived:
        query = query.filter(~AgentConversation.is_archived)
    if search:
        query = query.filter(AgentConversation.title.ilike(f"%{search}%"))
    total = query.count()
    items = query.order_by(AgentConversation.updated_at.desc()).offset(
        (page - 1) * page_size
    ).limit(page_size).all()
    return ConversationListResponse(
        items=[ConversationItem(id=c.id, title=c.title, updated_at=c.updated_at,
                                message_count=len(c.messages)) for c in items],
        has_next=(page * page_size) < total,
        active_total=total,
    )
# #endregion AgentChat.Api.ListConversations


# #region AgentChat.Api.SaveConversation [C:3] [TYPE Function] [SEMANTICS agent-chat,api,save]
# @ingroup AgentChat
# @BRIEF POST /api/agent/conversations/save — create or update conversation + messages.
# @PRE Service JWT with role=agent authenticates the Gradio container.
# @POST Conversation saved (upsert by conversation_id). Messages appended.
# @SIDE_EFFECT Writes to AgentConversation and AgentMessage tables.

@agent_router.post("/conversations/save")
async def save_conversation(
    body: SaveConversationRequest,
    db: Session = Depends(get_db),
):
    """Create or update a conversation. Called by Gradio agent after streaming."""
    conv = db.query(AgentConversation).filter(
        AgentConversation.id == body.conversation_id,
    ).first()
    # Use provided user_id or default to "admin"
    user_id = body.user_id or "admin"
    if not conv:
        conv = AgentConversation(
            id=body.conversation_id,
            user_id=user_id,
            title=body.title or "",
            created_at=datetime.utcnow(),
        )
        db.add(conv)
    conv.updated_at = datetime.utcnow()
    if body.title:
        conv.title = body.title

    # Save messages from payload
    if body.messages:
        for msg_data in body.messages:
            msg_id = msg_data.get("id", "")
            if not msg_id:
                continue
            # Check if message already exists (idempotent)
            existing = db.query(AgentMessage).filter(
                AgentMessage.id == msg_id,
            ).first()
            if not existing:
                msg = AgentMessage(
                    id=msg_id,
                    conversation_id=body.conversation_id,
                    role=msg_data.get("role", "user"),
                    text=msg_data.get("text", ""),
                    tool_calls=msg_data.get("tool_calls"),
                    attachments=msg_data.get("attachments"),
                    created_at=datetime.utcnow(),
                )
                db.add(msg)
        db.flush()

    db.commit()
    return {"saved": True, "conversation_id": body.conversation_id}
# #endregion AgentChat.Api.SaveConversation


# #region AgentChat.Api.GetHistory [C:3] [TYPE Function] [SEMANTICS agent-chat,api,history]
# @ingroup AgentChat
# @BRIEF GET /api/assistant/history — paginated messages for a conversation.
@router.get("/history", response_model=HistoryResponse)
async def get_history(
    conversation_id: str = Query(...),
    page: int = Query(1, ge=1),  # noqa: ARG001 — kept for API consistency
    page_size: int = Query(30, ge=1, le=100),  # noqa: ARG001 — kept for API consistency
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    conv = db.query(AgentConversation).filter(
        AgentConversation.id == conversation_id,
        AgentConversation.user_id == user.id,
    ).first()
    if not conv:
        raise HTTPException(status_code=404, detail="Conversation not found")
    messages = conv.messages
    return HistoryResponse(
        items=[MessageItem(id=m.id, conversation_id=m.conversation_id, role=m.role,
                           text=m.text, tool_calls=m.tool_calls,
                           attachments=m.attachments, created_at=m.created_at)
               for m in messages],
        has_next=False,
        conversation_id=conversation_id,
    )
# #endregion AgentChat.Api.GetHistory


# #region AgentChat.Api.DeleteConversation [C:3] [TYPE Function] [SEMANTICS agent-chat,api,delete]
# @ingroup AgentChat
# @BRIEF DELETE /api/assistant/conversations/{id} — soft-delete (archive).
@router.delete("/conversations/{conversation_id}", response_model=DeleteResponse)
async def delete_conversation(
    conversation_id: str,
    user=Depends(get_current_user),
    db: Session = Depends(get_db),
):
    conv = db.query(AgentConversation).filter(
        AgentConversation.id == conversation_id,
        AgentConversation.user_id == user.id,
    ).first()
    if not conv:
        raise HTTPException(status_code=404, detail="Conversation not found")
    conv.is_archived = True
    db.commit()
    return DeleteResponse(deleted=True)
# #endregion AgentChat.Api.DeleteConversation


# #region AgentChat.Api.ConversationsActive [C:2] [TYPE Function] [SEMANTICS agent-chat,api,active]
# @ingroup AgentChat
# @BRIEF GET /api/agent/conversations/active — multi-tab gate. Returns whether any agent session
#        is active for this user. Actual enforcement is Gradio's per-user in-memory lock.
# @RATIONALE FR-015 / FR-026: per-user concurrency enforced in Gradio handler via _user_locks dict.
#            This endpoint provides a client-side pre-check to avoid sending when another tab is active.
# @POST Response with {active: bool}. When active=true, the client should not send a new message.

@agent_router.get("/conversations/active")
async def check_active_session():
    # In-memory lock check is not accessible from REST. Return false to always allow;
    # actual enforcement happens in Gradio handler's _user_locks.
    return {"active": False}
# #endregion AgentChat.Api.ConversationsActive


# #region AgentChat.Api.LlmConfig [C:3] [TYPE Function] [SEMANTICS agent-chat,api,llm,config]
# @ingroup AgentChat
# @BRIEF GET /api/agent/llm-config — internal endpoint for Gradio agent to fetch LLM provider
#        configuration with decrypted API key. Gated by service JWT.
# @PRE Authenticated via service JWT (Authorization: Bearer <service_jwt> with role=agent).
# @POST Returns active LLM provider config: provider_type, base_url, api_key, default_model.
# @SIDE_EFFECT Decrypts API key from database.
# @RATIONALE Gradio container has no DB connection (FR-004 revised). It fetches LLM config
#            from FastAPI REST instead of requiring duplicate env vars.
from ...core.config_manager import ConfigManager
from ...core.database import get_db
from ...dependencies import get_config_manager
from ...services.llm_provider import LLMProviderService

@agent_router.get("/llm-config")
async def get_agent_llm_config(
    db: Session = Depends(get_db),
    config_manager: ConfigManager = Depends(get_config_manager),
):
    """Return active LLM provider config with decrypted API key.

    Internal endpoint — no user auth required. Gradio agent calls this at startup
    within the Docker network. Returns the provider configured in
    'assistant_planner_provider' setting, or first active provider as fallback.
    """
    service = LLMProviderService(db)
    providers = service.get_all_providers()

    # Priority 1: use provider from "Провайдер чат-бота" setting
    llm_settings = config_manager.get_config().settings.llm
    if isinstance(llm_settings, dict):
        preferred_id = llm_settings.get("assistant_planner_provider", "")
        if preferred_id:
            preferred = next((p for p in providers if p.id == preferred_id), None)
            if preferred:
                api_key = service.get_decrypted_api_key(preferred.id)
                if api_key:
                    return _make_provider_response(preferred, api_key)

    # Priority 2: first active provider
    active = next((p for p in providers if p.is_active), None)
    if not active:
        return {"configured": False, "reason": "no_active_provider"}
    api_key = service.get_decrypted_api_key(active.id)
    if not api_key:
        return {"configured": False, "reason": "invalid_api_key"}
    return _make_provider_response(active, api_key)


def _make_provider_response(provider, api_key: str) -> dict:
    """Build the provider config response dict."""
    return {
        "configured": True,
        "provider_type": provider.provider_type,
        "base_url": provider.base_url or "",
        "api_key": api_key,
        "default_model": provider.default_model or "gpt-4o-mini",
        "provider_name": provider.name,
    }
# #endregion AgentChat.Api.LlmConfig
# #endregion AgentChat.Api.Conversations
