from __future__ import annotations

import logging
from uuid import uuid4

from app.modules.agent.llm import AgentLlmService
from app.modules.chat.evidence_gate import CodeExplainEvidenceGate
from app.modules.chat.session_resolver import ChatSessionResolver
from app.modules.chat.task_store import TaskState, TaskStore
from app.modules.rag.explain import CodeExplainRetrieverV2, PromptBudgeter
from app.schemas.chat import ChatMessageRequest, TaskQueuedResponse, TaskResultType, TaskStatus

LOGGER = logging.getLogger(__name__)


class CodeExplainChatService:
    def __init__(
        self,
        retriever: CodeExplainRetrieverV2,
        llm: AgentLlmService,
        session_resolver: ChatSessionResolver,
        task_store: TaskStore,
        message_sink,
        budgeter: PromptBudgeter | None = None,
        evidence_gate: CodeExplainEvidenceGate | None = None,
    ) -> None:
        self._retriever = retriever
        self._llm = llm
        self._session_resolver = session_resolver
        self._task_store = task_store
        self._message_sink = message_sink
        self._budgeter = budgeter or PromptBudgeter()
        self._evidence_gate = evidence_gate or CodeExplainEvidenceGate()

    async def handle_message(self, request: ChatMessageRequest) -> TaskQueuedResponse:
        dialog_session_id, rag_session_id = self._session_resolver.resolve(request)
        task_id = str(uuid4())
        task = TaskState(task_id=task_id, status=TaskStatus.RUNNING)
        self._task_store.save(task)
        self._message_sink(dialog_session_id, "user", request.message, task_id=task_id)
        pack = self._retriever.build_pack(
            rag_session_id,
            request.message,
            file_candidates=[item.model_dump(mode="json") for item in request.files],
        )
        decision = self._evidence_gate.evaluate(pack)
        if decision.passed:
            prompt_input = self._budgeter.build_prompt_input(request.message, pack)
            answer = self._llm.generate(
                "code_explain_answer_v2",
                prompt_input,
                log_context="chat.code_explain.direct",
            ).strip()
        else:
            answer = decision.answer
        self._message_sink(dialog_session_id, "assistant", answer, task_id=task_id)
        task.status = TaskStatus.DONE
        task.result_type = TaskResultType.ANSWER
        task.answer = answer
        self._task_store.save(task)
        LOGGER.warning(
            "direct code explain response: task_id=%s rag_session_id=%s excerpts=%s missing=%s",
            task_id,
            rag_session_id,
            len(pack.code_excerpts),
            pack.missing,
        )
        return TaskQueuedResponse(
            task_id=task_id,
            status=TaskStatus.DONE.value,
        )
