from __future__ import annotations

from app.modules.rag.contracts import RagDocument, RagSource
from app.modules.rag.indexing.docs.chunkers.markdown_chunker import MarkdownDocChunker
from app.modules.rag.indexing.docs.classifier import DocsClassifier
from app.modules.rag.indexing.docs.content_parser import DocsContentParser
from app.modules.rag.indexing.docs.document_builder import DocsDocumentBuilder
from app.modules.rag.indexing.docs.entity_extractor import DocsEntityExtractor
from app.modules.rag.indexing.docs.fact_extractor import DocsFactExtractor
from app.modules.rag.indexing.docs.file_filter import DocsFileFilter
from app.modules.rag.indexing.docs.frontmatter_parser import DocsFrontmatterParser
from app.modules.rag.indexing.docs.relation_extractor import DocsRelationExtractor
from app.modules.rag.indexing.docs.workflow_extractor import DocsWorkflowExtractor


class DocsIndexingPipeline:
    def __init__(self) -> None:
        self._filter = DocsFileFilter()
        self._classifier = DocsClassifier()
        self._chunker = MarkdownDocChunker()
        self._builder = DocsDocumentBuilder()
        self._frontmatter = DocsFrontmatterParser()
        self._content = DocsContentParser()
        self._facts = DocsFactExtractor()
        self._entities = DocsEntityExtractor()
        self._workflows = DocsWorkflowExtractor()
        self._relations = DocsRelationExtractor()

    def supports(self, path: str) -> bool:
        return self._filter.should_index(path)

    def index_file(self, *, repo_id: str, commit_sha: str | None, path: str, content: str) -> list[RagDocument]:
        source = RagSource(repo_id=repo_id, commit_sha=commit_sha, path=path)
        frontmatter, body = self._frontmatter.split(content)
        doc_kind = self._classifier.classify(path)
        parsed = self._content.parse(frontmatter, body, fallback_title=self._fallback_title(path))
        sections = self._chunker.chunk(parsed.body or body)
        docs: list[RagDocument] = []
        docs.append(
            self._builder.build_document_catalog(
                source,
                parsed.frontmatter,
                parsed.summary,
                doc_kind,
                fallback_title=parsed.title,
            )
        )
        for section in sections:
            docs.append(self._builder.build_doc_chunk(source, section, parsed.frontmatter, doc_kind))
        document_id = str(parsed.frontmatter.get("id") or source.path)
        for fact in self._facts.extract(parsed.frontmatter, sections):
            docs.append(
                self._builder.build_fact(
                    source,
                    subject_id=fact["subject_id"] or document_id,
                    predicate=fact["predicate"],
                    obj=fact["object"],
                    object_ref=fact.get("object_ref"),
                    anchor=fact["anchor"],
                )
            )
        for entity in self._entities.extract(parsed.frontmatter):
            docs.append(self._builder.build_entity_record(source, parsed.frontmatter, entity))
        for workflow in self._workflows.extract(parsed.detail_sections):
            docs.append(self._builder.build_workflow_record(source, parsed.frontmatter, workflow))
        for edge in self._relations.extract(parsed.frontmatter, source_id=document_id):
            docs.append(self._builder.build_relation_record(source, edge))
        return docs

    def _fallback_title(self, path: str) -> str:
        tail = path.rsplit("/", 1)[-1]
        stem = tail.rsplit(".", 1)[0]
        return stem.replace("-", " ").replace("_", " ").strip().title()
