import logging
import os
from pathlib import Path

from app.core.agent.processes import V1Process, V2Process
from app.core.agent.processes.v2 import V2IntentRouter
from app.core.agent.utils.process_v2.evidence.assembler import DocsEvidenceAssembler
from app.core.agent.utils.process_v2.plan_resolver import V2RetrievalPolicyResolver
from app.core.agent.utils.process_v2.rag_retrieval import V2RagRetrievalAdapter
from app.core.rag.retrieval.session_retriever import RagSessionRetriever
from app.core.agent.runtime import AgentRuntime, ProcessRegistry, ProcessRunner, RuntimeEventPublisher
from app.core.agent.utils.llm import AgentLlmService, PromptLoader
from app.core.api.module import ApiModule
from app.core.api.application.session_bootstrap_service import SessionBootstrapService
from app.core.api.application.request_service import RequestService
from app.core.api.application.session_service import SessionService
from app.core.api.application.stream_service import StreamService
from app.core.api.infrastructure.ids.request_id_factory import RequestIdFactory
from app.core.api.infrastructure.ids.session_id_factory import SessionIdFactory
from app.core.api.infrastructure.stores.in_memory_request_store import InMemoryRequestStore
from app.core.api.infrastructure.stores.in_memory_session_store import InMemorySessionStore
from app.core.api.infrastructure.streaming.sse_event_channel import SseEventChannel
from app.core.rag.module import RagModule
from app.core.rag.embedding.gigachat_embedder import GigaChatEmbedder
from app.core.rag.persistence import RagRepository
from app.core.shared.database import DatabaseReadiness, bootstrap_database
from app.core.shared.messaging import EventBus
from app.core.shared.story_context import StoryContextSchemaRepository
from app.infra.observability import RequestTraceLogger
from app.core.shared.resilience import RetryExecutor


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

        self.rag = RagModule(
            event_bus=self.events,
            retry=self.retry,
            repository=self.rag_repository,
            ensure_ready=self.database_readiness.require_ready,
        )
        from app.core.shared.gigachat.client import GigaChatClient
        from app.core.shared.gigachat.settings import GigaChatSettings
        from app.core.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/processes/v1/workflow/flow_main/prompts.yml"
        )
        _v2_prompt_loader = PromptLoader(
            [
                Path(__file__).resolve().parent
                / "agent/processes/v2/workflows/doc_explain_summary/steps/prompts/prompts.yml",
                Path(__file__).resolve().parent
                / "agent/processes/v2/workflows/general_qa_summary/steps/prompts/prompts.yml",
                Path(__file__).resolve().parent
                / "agent/processes/v2/workflows/doc_update_from_feature/steps/prompts/prompts.yml",
                Path(__file__).resolve().parent / "agent/processes/v2/intent_router/routers/prompts.yml",
            ]
        )
        self._v1_llm = AgentLlmService(client=_giga_client, prompts=_v1_prompt_loader)
        self._v2_llm = AgentLlmService(client=_giga_client, prompts=_v2_prompt_loader)
        _v2_embedder = GigaChatEmbedder(_giga_client)
        _v2_rag_retriever = RagSessionRetriever(repository=self.rag_repository, embedder=_v2_embedder)
        _v2_rag_adapter = V2RagRetrievalAdapter(_v2_rag_retriever)
        _v2_evidence = DocsEvidenceAssembler()
        _v2_policy = V2RetrievalPolicyResolver()
        _doc_rules_enabled = os.getenv("V2_DOC_RULES_ENABLED", "true").strip().lower() in {"1", "true", "yes"}

        self.agent_sessions = InMemorySessionStore()
        self.agent_requests = InMemoryRequestStore()
        self.agent_events = SseEventChannel()
        self.agent_trace_logger = RequestTraceLogger(Path("runtime_traces/agent_requests"))
        _publisher = RuntimeEventPublisher(self.agent_events, self.agent_trace_logger)
        _session_service = SessionService(
            store=self.agent_sessions,
            ids=SessionIdFactory(),
        )
        _session_bootstrap = SessionBootstrapService(_session_service, self.rag)
        _process_registry = ProcessRegistry(
            [
                V1Process(self._v1_llm),
                V2Process(
                    self._v2_llm,
                    policy_resolver=_v2_policy,
                    rag_adapter=_v2_rag_adapter,
                    evidence_assembler=_v2_evidence,
                    router=V2IntentRouter(llm=self._v2_llm),
                    workflow_llm_enabled=True,
                    doc_rules_enabled=_doc_rules_enabled,
                ),
            ]
        )
        _runtime = AgentRuntime(
            request_store=self.agent_requests,
            sessions=_session_service,
            process_registry=_process_registry,
            process_runner=ProcessRunner(),
            publisher=_publisher,
            trace_logger=self.agent_trace_logger,
        )
        _request_service = RequestService(
            request_store=self.agent_requests,
            request_ids=RequestIdFactory(),
            sessions=_session_service,
            runtime=_runtime,
        )
        self.api = ApiModule(
            session_bootstrap=_session_bootstrap,
            requests=_request_service,
            streams=StreamService(self.agent_events, request_exists=lambda request_id: self.agent_requests.get(request_id) is not None),
            rag=self.rag,
        )

    def startup(self) -> None:
        try:
            bootstrap_database(
                self.rag_repository,
                self.story_context_schema_repository,
            )
        except Exception as exc:
            logging.exception("Database bootstrap failed. Starting backend in degraded mode.")
            self.database_readiness.mark_unavailable(exc)
            return
        self.database_readiness.mark_ready()

    def health_payload(self) -> dict[str, str]:
        return self.database_readiness.health_payload()
