# #region AssistantRoutes [C:5] [TYPE Module] [SEMANTICS assistant, api, route, chat, execution]
# @BRIEF FastAPI route handlers for the assistant API — message sending, confirmation, conversation management.
# @LAYER API
# @RELATION DEPENDS_ON -> [AssistantSchemas]
# @RELATION DEPENDS_ON -> [AssistantHistory]
# @RELATION DEPENDS_ON -> [AssistantCommandParser]
# @RELATION DEPENDS_ON -> [AssistantLlmPlanner]
# @RELATION DEPENDS_ON -> [AssistantDatasetReview]
# @RELATION DEPENDS_ON -> [AssistantDispatch]
# @RELATION DISPATCHES -> [AssistantAdminRoutes]
# @INVARIANT  Risky operations are never executed without valid confirmation token.

from __future__ import annotations

from datetime import UTC, datetime
import uuid

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

from src.core.config_manager import ConfigManager
from src.core.database import get_db
from src.core.logger import belief_scope, logger
from src.core.task_manager import TaskManager
from src.dependencies import (
    get_config_manager,
    get_current_user,
    get_task_manager,
)
from src.schemas.auth import User

from ._command_parser import _parse_command
from ._dataset_review import (
    _load_dataset_review_context,
    _plan_dataset_review_intent,
)
from ._dispatch import (
    _async_confirmation_summary,
    _clarification_text_for_intent,
)
from ._tool_registry import dispatch
from ._history import (
    _append_history,
    _audit,
    _load_confirmation_from_db,
    _persist_audit,
    _persist_confirmation,
    _persist_message,
    _resolve_or_create_conversation,
    _update_confirmation_state,
)
from ._llm_planner import _build_tool_catalog
from ._llm_planner_intent import _authorize_intent, _plan_intent_with_llm
from ._tool_registry import get_safe_ops
from ._schemas import (
    CONFIRMATIONS,
    AssistantAction,
    AssistantMessageRequest,
    AssistantMessageResponse,
    ConfirmationRecord,
)

router = APIRouter(tags=["Assistant"])


@router.post("/messages", response_model=AssistantMessageResponse)
# #region send_message [C:5] [TYPE Function]
# @BRIEF Parse assistant command, enforce safety gates, and dispatch executable intent.
# @DATA_CONTRACT  Input[AssistantMessageRequest,User,TaskManager,ConfigManager,Session] -> Output[AssistantMessageResponse]
# @RELATION DEPENDS_ON -> [_plan_intent_with_llm]
# @RELATION DEPENDS_ON -> [_parse_command]
# @RELATION DEPENDS_ON -> [dispatch]
# @RELATION DEPENDS_ON -> [_append_history]
# @RELATION DEPENDS_ON -> [_persist_message]
# @RELATION DEPENDS_ON -> [_audit]
# @SIDE_EFFECT  Persists chat/audit state, mutates in-memory conversation and confirmation stores, and may create confirmation records.
# @PRE  Authenticated user is available and message text is non-empty.
# @POST  Response state is one of clarification/confirmation/started/success/denied/failed.
# @INVARIANT  non-safe operations are gated with confirmation before execution from this endpoint.
async def send_message(request: AssistantMessageRequest, current_user: User=Depends(get_current_user), task_manager: TaskManager=Depends(get_task_manager), config_manager: ConfigManager=Depends(get_config_manager), db: Session=Depends(get_db)):
    with belief_scope('send_message'):
        logger.reason('Belief protocol reasoning checkpoint for send_message')
        user_id = current_user.id
        dataset_review_context = _load_dataset_review_context(request.dataset_review_session_id, current_user, db)
        conversation_id = _resolve_or_create_conversation(user_id, request.conversation_id, db)
        _append_history(user_id, conversation_id, 'user', request.message)
        _persist_message(db, user_id, conversation_id, 'user', request.message)
        tools_catalog = _build_tool_catalog(current_user, config_manager, db)
        intent = None
        try:
            intent = await _plan_intent_with_llm(request.message, tools_catalog, db, config_manager)
        except Exception as exc:
            logger.warning(f'[assistant.planner][fallback] Planner error: {exc}')
        if not intent:
            intent = _parse_command(request.message, config_manager)
        if dataset_review_context:
            dataset_review_intent = _plan_dataset_review_intent(request.message, dataset_review_context)
            if dataset_review_intent is not None:
                intent = dataset_review_intent
        confidence = float(intent.get('confidence', 0.0))
        if intent.get('domain') == 'unknown' or confidence < 0.6:
            # Use LLM-generated clarification question when available (from _plan_intent_with_llm),
            # otherwise fall back to deterministic text.
            text = (
                intent.get('clarification')
                or 'Команда неоднозначна. Уточните, что вы хотите сделать.'
            )
            _append_history(user_id, conversation_id, 'assistant', text, state='needs_clarification')
            _persist_message(db, user_id, conversation_id, 'assistant', text, state='needs_clarification', metadata={'intent': intent})
            audit_payload = {'decision': 'needs_clarification', 'message': request.message, 'intent': intent, 'dataset_review_session_id': request.dataset_review_session_id}
            _audit(user_id, audit_payload)
            _persist_audit(db, user_id, audit_payload, conversation_id)
            logger.reflect('Belief protocol postcondition checkpoint for send_message')
            return AssistantMessageResponse(conversation_id=conversation_id, response_id=str(uuid.uuid4()), state='needs_clarification', text=text, intent=intent, actions=[AssistantAction(type='rephrase', label='Rephrase command')], created_at=datetime.now(UTC))
        try:
            _authorize_intent(intent, current_user)
            operation = intent.get('operation')
            if operation not in get_safe_ops():
                confirmation_id = str(uuid.uuid4())
                confirm = ConfirmationRecord(id=confirmation_id, user_id=user_id, conversation_id=conversation_id, intent=intent, dispatch={'intent': intent}, expires_at=datetime.now(UTC) + __import__('datetime').timedelta(minutes=5), created_at=datetime.now(UTC))
                CONFIRMATIONS[confirmation_id] = confirm
                _persist_confirmation(db, confirm)
                text = await _async_confirmation_summary(intent, config_manager, db)
                _append_history(user_id, conversation_id, 'assistant', text, state='needs_confirmation', confirmation_id=confirmation_id)
                _persist_message(db, user_id, conversation_id, 'assistant', text, state='needs_confirmation', confirmation_id=confirmation_id, metadata={'intent': intent, 'dataset_review_context': dataset_review_context, 'actions': [{'type': 'confirm', 'label': '✅ Подтвердить', 'target': confirmation_id}, {'type': 'cancel', 'label': '❌ Отменить', 'target': confirmation_id}]})
                audit_payload = {'decision': 'needs_confirmation', 'message': request.message, 'intent': intent, 'confirmation_id': confirmation_id, 'dataset_review_session_id': request.dataset_review_session_id}
                _audit(user_id, audit_payload)
                _persist_audit(db, user_id, audit_payload, conversation_id)
                logger.reflect('Belief protocol postcondition checkpoint for send_message')
                return AssistantMessageResponse(conversation_id=conversation_id, response_id=str(uuid.uuid4()), state='needs_confirmation', text=text, intent=intent, confirmation_id=confirmation_id, actions=[AssistantAction(type='confirm', label='✅ Подтвердить', target=confirmation_id), AssistantAction(type='cancel', label='❌ Отменить', target=confirmation_id)], created_at=datetime.now(UTC))
            text, task_id, actions = await dispatch(intent.get('operation'), intent, current_user, task_manager, config_manager, db)
            state = 'started' if task_id else 'success'
            _append_history(user_id, conversation_id, 'assistant', text, state=state, task_id=task_id)
            _persist_message(db, user_id, conversation_id, 'assistant', text, state=state, task_id=task_id, metadata={'intent': intent, 'dataset_review_context': dataset_review_context, 'actions': [a.model_dump() for a in actions]})
            audit_payload = {'decision': 'executed', 'message': request.message, 'intent': intent, 'task_id': task_id, 'dataset_review_session_id': request.dataset_review_session_id}
            _audit(user_id, audit_payload)
            _persist_audit(db, user_id, audit_payload, conversation_id)
            logger.reflect('Belief protocol postcondition checkpoint for send_message')
            return AssistantMessageResponse(conversation_id=conversation_id, response_id=str(uuid.uuid4()), state=state, text=text, intent=intent, task_id=task_id, actions=actions, created_at=datetime.now(UTC))
        except HTTPException as exc:
            detail_text = str(exc.detail)
            is_clarification_error = exc.status_code in (400, 422) and (detail_text.lower().startswith('missing') or 'укажите' in detail_text.lower() or 'выберите' in detail_text.lower())
            if exc.status_code == status.HTTP_403_FORBIDDEN:
                state = 'denied'
            elif is_clarification_error:
                state = 'needs_clarification'
            else:
                state = 'failed'
            text = _clarification_text_for_intent(intent, detail_text) if state == 'needs_clarification' else detail_text
            _append_history(user_id, conversation_id, 'assistant', text, state=state)
            _persist_message(db, user_id, conversation_id, 'assistant', text, state=state, metadata={'intent': intent})
            audit_payload = {'decision': state, 'message': request.message, 'intent': intent, 'error': text, 'dataset_review_session_id': request.dataset_review_session_id}
            _audit(user_id, audit_payload)
            _persist_audit(db, user_id, audit_payload, conversation_id)
            logger.reflect('Belief protocol postcondition checkpoint for send_message')
            return AssistantMessageResponse(conversation_id=conversation_id, response_id=str(uuid.uuid4()), state=state, text=text, intent=intent, actions=[AssistantAction(type='rephrase', label='Rephrase command')] if state == 'needs_clarification' else [], created_at=datetime.now(UTC))


# #endregion send_message


@router.post(
    "/confirmations/{confirmation_id}/confirm", response_model=AssistantMessageResponse
)
# #region confirm_operation [C:2] [TYPE Function]
# @BRIEF Execute previously requested risky operation after explicit user confirmation.
# @PRE  confirmation_id exists, belongs to current user, is pending, and not expired.
# @POST  Confirmation state becomes consumed and operation result is persisted in history.
async def confirm_operation(
    confirmation_id: str,
    current_user: User = Depends(get_current_user),
    task_manager: TaskManager = Depends(get_task_manager),
    config_manager: ConfigManager = Depends(get_config_manager),
    db: Session = Depends(get_db),
):
    with belief_scope("assistant.confirm"):
        record = CONFIRMATIONS.get(confirmation_id)
        if not record:
            record = _load_confirmation_from_db(db, confirmation_id)
            if record:
                CONFIRMATIONS[confirmation_id] = record
            else:
                raise HTTPException(status_code=404, detail="Confirmation not found")

        if record.user_id != current_user.id:
            raise HTTPException(
                status_code=403, detail="Confirmation does not belong to current user"
            )

        if record.state != "pending":
            raise HTTPException(
                status_code=400, detail=f"Confirmation already {record.state}"
            )

        if datetime.now(UTC) > record.expires_at:
            record.state = "expired"
            _update_confirmation_state(db, confirmation_id, "expired")
            raise HTTPException(status_code=400, detail="Confirmation expired")

        intent = record.intent
        text, task_id, actions = await dispatch(
            intent.get('operation'), intent, current_user, task_manager, config_manager, db
        )
        record.state = "consumed"
        _update_confirmation_state(db, confirmation_id, "consumed")

        _append_history(
            current_user.id,
            record.conversation_id,
            "assistant",
            text,
            state="started" if task_id else "success",
            task_id=task_id,
        )
        _persist_message(
            db,
            current_user.id,
            record.conversation_id,
            "assistant",
            text,
            state="started" if task_id else "success",
            task_id=task_id,
            metadata={"intent": intent, "confirmation_id": confirmation_id},
        )
        audit_payload = {
            "decision": "confirmed_execute",
            "confirmation_id": confirmation_id,
            "task_id": task_id,
            "intent": intent,
        }
        _audit(current_user.id, audit_payload)
        _persist_audit(db, current_user.id, audit_payload, record.conversation_id)

        return AssistantMessageResponse(
            conversation_id=record.conversation_id,
            response_id=str(uuid.uuid4()),
            state="started" if task_id else "success",
            text=text,
            intent=intent,
            task_id=task_id,
            actions=actions,
            created_at=datetime.now(UTC),
        )



# #endregion confirm_operation


@router.post(
    "/confirmations/{confirmation_id}/cancel", response_model=AssistantMessageResponse
)
# #region cancel_operation [C:2] [TYPE Function]
# @BRIEF Cancel pending risky operation and mark confirmation token as cancelled.
# @PRE  confirmation_id exists, belongs to current user, and is still pending.
# @POST  Confirmation becomes cancelled and cannot be executed anymore.
async def cancel_operation(
    confirmation_id: str,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    with belief_scope("assistant.cancel"):
        record = CONFIRMATIONS.get(confirmation_id)
        if not record:
            record = _load_confirmation_from_db(db, confirmation_id)
            if record:
                CONFIRMATIONS[confirmation_id] = record
            else:
                raise HTTPException(status_code=404, detail="Confirmation not found")

        if record.user_id != current_user.id:
            raise HTTPException(
                status_code=403, detail="Confirmation does not belong to current user"
            )

        if record.state != "pending":
            raise HTTPException(
                status_code=400, detail=f"Confirmation already {record.state}"
            )

        record.state = "cancelled"
        _update_confirmation_state(db, confirmation_id, "cancelled")
        text = "Операция отменена. Выполнение не запускалось."
        _append_history(
            current_user.id,
            record.conversation_id,
            "assistant",
            text,
            state="success",
            confirmation_id=confirmation_id,
        )
        _persist_message(
            db,
            current_user.id,
            record.conversation_id,
            "assistant",
            text,
            state="success",
            confirmation_id=confirmation_id,
            metadata={"intent": record.intent},
        )
        audit_payload = {
            "decision": "cancelled",
            "confirmation_id": confirmation_id,
            "intent": record.intent,
        }
        _audit(current_user.id, audit_payload)
        _persist_audit(db, current_user.id, audit_payload, record.conversation_id)

        return AssistantMessageResponse(
            conversation_id=record.conversation_id,
            response_id=str(uuid.uuid4()),
            state="success",
            text=text,
            intent=record.intent,
            confirmation_id=confirmation_id,
            actions=[],
            created_at=datetime.now(UTC),
        )



# #endregion cancel_operation


# #endregion AssistantRoutes
