from __future__ import annotations

from datetime import datetime, timezone

from app.core.exceptions import AppError
from app.modules.api.domain.models.agent_request import AgentRequest
from app.modules.api.domain.models.agent_session import AgentSession
from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger
from app.modules.agent.observability.module_trace import RequestTraceContext
from app.modules.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore
from app.modules.agent.orchestration.context.execution_context import ExecutionContext
from app.modules.agent.orchestration.messaging.client_message_publisher import ClientMessagePublisher
from app.modules.agent.orchestration.processes.registry import ProcessRegistry
from app.modules.agent.orchestration.runtime.process_runner import ProcessRunner
from app.schemas.common import ErrorPayload, ModuleName
from app.schemas.orchestration import RequestExecutionStatus


class OrchestrationFacade:
    def __init__(
        self,
        request_store: InMemoryRequestStore,
        process_registry: ProcessRegistry,
        process_runner: ProcessRunner,
        publisher: ClientMessagePublisher,
        trace_logger: RequestTraceLogger,
    ) -> None:
        self._request_store = request_store
        self._process_registry = process_registry
        self._process_runner = process_runner
        self._publisher = publisher
        self._trace_logger = trace_logger

    async def run(self, request: AgentRequest, session: AgentSession) -> None:
        try:
            process = self._process_registry.get(request.process_version)
            if process is None:
                raise AppError("process_not_found", f"Unsupported process version: {request.process_version}", ModuleName.AGENT)
            request.status = RequestExecutionStatus.RUNNING
            self._request_store.save(request)
            self._trace_logger.start_request(request, session)
            context = ExecutionContext(
                request=request,
                session=session,
                publisher=self._publisher,
                trace_logger=self._trace_logger,
                trace=RequestTraceContext(request_id=request.request_id, logger=self._trace_logger),
            )
            await self._process_runner.run(context, process.steps())
            request.status = RequestExecutionStatus.DONE
            request.completed_at = datetime.now(timezone.utc)
            self._request_store.save(request)
            self._trace_logger.complete_request(request)
        except Exception as exc:
            request.status = RequestExecutionStatus.ERROR
            request.completed_at = datetime.now(timezone.utc)
            if isinstance(exc, AppError):
                request.error = ErrorPayload(code=exc.code, desc=exc.desc, module=exc.module)
            else:
                request.error = ErrorPayload(
                    code="api_runtime_error",
                    desc="Agent request failed unexpectedly.",
                    module=ModuleName.AGENT,
                )
            self._request_store.save(request)
            self._trace_logger.fail_request(request)
            await self._publisher.publish_status(
                request.request_id,
                "orchestrator",
                "Во время обработки запроса произошла ошибка.",
                {"code": request.error.code},
            )
