"""RAG module composition and application-facing operations."""

from __future__ import annotations

from app.infra.exceptions import AppError
from app.core.rag.embedding.gigachat_embedder import GigaChatEmbedder
from app.core.rag.indexing import IndexJob, IndexJobStore, IndexingOrchestrator, RagService
from app.core.rag.persistence import RagRepository, RagSession, RagSessionStore
from app.core.rag.retrieval import TextChunker
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
from app.core.shared.messaging import EventBus
from app.core.shared.resilience import RetryExecutor
from app.schemas.common import ModuleName


class RagModule:
    def __init__(self, event_bus: EventBus, retry: RetryExecutor, repository: RagRepository, ensure_ready=None) -> None:
        self._events = event_bus
        self._ensure_ready = ensure_ready or (lambda: None)
        settings = GigaChatSettings.from_env()
        token_provider = GigaChatTokenProvider(settings)
        client = GigaChatClient(settings, token_provider)
        embedder = GigaChatEmbedder(client)
        self.repository = repository
        self.rag = RagService(embedder=embedder, repository=repository, chunker=TextChunker())
        self.sessions = RagSessionStore(repository)
        self.jobs = IndexJobStore(repository)
        self.indexing = IndexingOrchestrator(store=self.jobs, rag=self.rag, events=event_bus, retry=retry)

    async def create_session(self, project_id: str, files: list[dict]) -> tuple[RagSession, IndexJob]:
        self._ensure_ready()
        session = self.sessions.create(project_id)
        job = await self.indexing.enqueue_snapshot(rag_session_id=session.rag_session_id, files=files)
        return session, job

    def get_session_job(self, rag_session_id: str, index_job_id: str) -> IndexJob:
        self._ensure_ready()
        job = self._require_job(index_job_id)
        if job.rag_session_id != rag_session_id:
            raise AppError("not_found", f"Index job not found: {index_job_id}", ModuleName.RAG)
        return job

    async def subscribe_session_job_events(self, rag_session_id: str, index_job_id: str):
        job = self.get_session_job(rag_session_id, index_job_id)
        return job.index_job_id, await self._events.subscribe(job.index_job_id, replay=True)

    async def unsubscribe_job_events(self, index_job_id: str, queue) -> None:
        await self._events.unsubscribe(index_job_id, queue)

    def _require_job(self, index_job_id: str) -> IndexJob:
        job = self.jobs.get(index_job_id)
        if not job:
            raise AppError("not_found", f"Index job not found: {index_job_id}", ModuleName.RAG)
        return job
