from pathlib import Path

from app.modules.agent.llm import AgentLlmService
from app.modules.agent.llm.prompt_loader import PromptLoader
from app.modules.agent.intent_router_v2 import IntentRouterV2
from app.modules.agent.runtime import DocsQAPipelineRunner
from app.modules.agent.runtime.steps.retrieval import RuntimeRepoContextFactory, RuntimeRetrievalAdapter
from app.modules.agent.task_runtime import ContextEnrichmentService, DocumentationTemplateRegistry, TaskRuntimeContextBuilder
from app.modules.agent.task_runtime.workflows import (
    DocumentationGenerationWorkflow,
    DocsQaWorkflow,
    FallbackWorkflow,
    OpenApiWorkflow,
)
from app.modules.agent.task_runtime.workflows.general_qa import GeneralQaWorkflow
from app.modules.api.module import ApiModule
from app.modules.api.application.request_service import RequestService
from app.modules.api.application.session_service import SessionService
from app.modules.api.application.stream_service import StreamService
from app.modules.api.infrastructure.ids.request_id_factory import RequestIdFactory
from app.modules.api.infrastructure.ids.session_id_factory import SessionIdFactory
from app.modules.api.infrastructure.logging.request_trace_logger import RequestTraceLogger
from app.modules.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore
from app.modules.api.infrastructure.stores.in_memory_session_store import InMemorySessionStore
from app.modules.api.infrastructure.streaming.sse_event_channel import SseEventChannel
from app.modules.agent.orchestration.facade import OrchestrationFacade
from app.modules.agent.orchestration.adapters.intent_router_adapter import IntentRouterAdapter
from app.modules.agent.orchestration.adapters.llm_chat_adapter import LlmChatAdapter
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.processes.v1.process import V1Process
from app.modules.agent.orchestration.processes.v1.prompt_payload_builder import V1PromptPayloadBuilder
from app.modules.agent.orchestration.processes.v1.simple_llm_workflow import SimpleLlmWorkflow
from app.modules.agent.orchestration.processes.v1.steps.bootstrap_step import BootstrapStep
from app.modules.agent.orchestration.processes.v1.steps.execute_llm_workflow_step import ExecuteLlmWorkflowStep
from app.modules.agent.orchestration.processes.v1.steps.finalize_step import FinalizeStep
from app.modules.agent.orchestration.processes.v2.prompt_payload_builder import V2PromptPayloadBuilder
from app.modules.agent.orchestration.processes.v2.prompt_selector import V2PromptSelector
from app.modules.agent.orchestration.processes.v2.process import V2Process
from app.modules.agent.orchestration.processes.v2.steps.execute_documentation_workflow_step import ExecuteDocumentationWorkflowStep
from app.modules.agent.orchestration.processes.v2.steps.execute_fallback_workflow_step import ExecuteFallbackWorkflowStep
from app.modules.agent.orchestration.processes.v2.steps.execute_general_qa_workflow_step import ExecuteGeneralQaWorkflowStep
from app.modules.agent.orchestration.processes.v2.steps.execute_openapi_workflow_step import ExecuteOpenApiWorkflowStep
from app.modules.agent.orchestration.processes.v2.steps.route_intent_step import RouteIntentStep
from app.modules.agent.orchestration.runtime.process_runner import ProcessRunner
from app.modules.rag.persistence.repository import RagRepository
from app.modules.agent.runtime.story_context_repository import StoryContextRepository, StoryContextSchemaRepository
from app.modules.rag.module import RagModule, RagRepoModule
from app.modules.shared.bootstrap import bootstrap_database
from app.modules.shared.event_bus import EventBus
from app.modules.shared.retry_executor import RetryExecutor


class ModularApplication:
    def __init__(self) -> None:
        self.events = EventBus()
        self.retry = RetryExecutor()
        self.rag_repository = RagRepository()
        self.story_context_schema_repository = StoryContextSchemaRepository()
        self.story_context_repository = StoryContextRepository()

        self.rag = RagModule(event_bus=self.events, retry=self.retry, repository=self.rag_repository)
        self.rag_repo = RagRepoModule(
            story_context_repository=self.story_context_repository,
            rag_repository=self.rag_repository,
        )
        from app.modules.shared.gigachat.client import GigaChatClient
        from app.modules.shared.gigachat.settings import GigaChatSettings
        from app.modules.shared.gigachat.token_provider import GigaChatTokenProvider

        _giga_settings = GigaChatSettings.from_env()
        _giga_client = GigaChatClient(_giga_settings, GigaChatTokenProvider(_giga_settings))
        _v1_prompt_loader = PromptLoader(Path(__file__).resolve().parent / "agent/orchestration/processes/v1/prompts.yml")
        _v2_prompt_loader = PromptLoader(Path(__file__).resolve().parent / "agent/orchestration/processes/v2/prompts.yml")
        self._agent_llm_v1 = AgentLlmService(client=_giga_client, prompts=_v1_prompt_loader)
        self._agent_llm_v2 = AgentLlmService(client=_giga_client, prompts=_v2_prompt_loader)
        _router = IntentRouterV2()
        _retrieval = RuntimeRetrievalAdapter(self.rag_repository)
        _repo_context_factory = RuntimeRepoContextFactory()
        _docs_runner = DocsQAPipelineRunner(
            router=_router,
            retrieval_adapter=_retrieval,
            repo_context=_repo_context_factory.build(),
            llm=self._agent_llm_v2,
            prompt_selector=V2PromptSelector(),
            prompt_payload_builder=V2PromptPayloadBuilder(),
        )
        _task_context_builder = TaskRuntimeContextBuilder(_repo_context_factory)
        _context_enrichment = ContextEnrichmentService()
        _docs_workflow = DocsQaWorkflow(_docs_runner)
        _openapi_workflow = OpenApiWorkflow(_docs_runner)
        _general_qa_workflow = GeneralQaWorkflow(_docs_runner)
        _fallback_workflow = FallbackWorkflow(self._agent_llm_v2)
        _docs_generation_workflow = DocumentationGenerationWorkflow(self._agent_llm_v2, DocumentationTemplateRegistry())
        self._docs_generation_workflow = _docs_generation_workflow

        self.agent_sessions = InMemorySessionStore()
        self.agent_requests = InMemoryRequestStore()
        self.agent_events = SseEventChannel()
        self.agent_trace_logger = RequestTraceLogger(Path("runtime_traces/agent_requests"))
        _publisher = ClientMessagePublisher(self.agent_events, self.agent_trace_logger)
        _v1_workflow = SimpleLlmWorkflow(
            LlmChatAdapter(self._agent_llm_v1, prompt_name="simple_llm_answer"),
            V1PromptPayloadBuilder(),
        )
        _process_registry = ProcessRegistry(
            V1Process([BootstrapStep(), ExecuteLlmWorkflowStep(_v1_workflow), FinalizeStep()]),
            V2Process(
                [
                    BootstrapStep(),
                    RouteIntentStep(IntentRouterAdapter(_router), _task_context_builder, _context_enrichment),
                    ExecuteDocumentationWorkflowStep(_docs_workflow, "workflow_documentation_explain"),
                    ExecuteOpenApiWorkflowStep(_openapi_workflow, "workflow_openapi_generation"),
                    ExecuteGeneralQaWorkflowStep(_general_qa_workflow, "workflow_general_qa"),
                    ExecuteFallbackWorkflowStep(_fallback_workflow, "workflow_fallback"),
                    FinalizeStep(),
                ]
            ),
        )
        _orchestration = OrchestrationFacade(
            request_store=self.agent_requests,
            process_registry=_process_registry,
            process_runner=ProcessRunner(),
            publisher=_publisher,
            trace_logger=self.agent_trace_logger,
        )
        _session_service = SessionService(
            store=self.agent_sessions,
            ids=SessionIdFactory(),
            rag_session_exists=lambda rag_session_id: self.rag.sessions.get(rag_session_id) is not None,
        )
        _request_service = RequestService(
            request_store=self.agent_requests,
            request_ids=RequestIdFactory(),
            sessions=_session_service,
            orchestration=_orchestration,
        )
        self.api = ApiModule(
            sessions=_session_service,
            requests=_request_service,
            streams=StreamService(self.agent_events, request_exists=lambda request_id: self.agent_requests.get(request_id) is not None),
        )

    def startup(self) -> None:
        bootstrap_database(
            self.rag_repository,
            self.story_context_schema_repository,
        )
