"""Docs service — index and search markdown documentation files."""
from __future__ import annotations

import re
from pathlib import Path
from typing import Any

import frontmatter  # python-frontmatter — already a project dependency


class DocsService:
    EXTENSIONS = {".md", ".mdx"}

    def __init__(self, paths: list[str]) -> None:
        self._roots = [Path(p) for p in paths if Path(p).exists()]

    # ── MDX conversion ────────────────────────────────────────────────

    @staticmethod
    def mdx_to_md(text: str) -> str:
        """Strip JSX/import statements from MDX, return clean Markdown."""
        post = frontmatter.loads(text)
        content: str = post.content
        metadata: dict[str, Any] = post.metadata  # type: ignore[assignment]

        # Strip ES module import/export lines
        content = re.sub(r"^(import|export)\s+.*$", "", content, flags=re.MULTILINE)

        # Strip self-closing JSX tags: <Component prop="x" />
        # [A-Z] prefix distinguishes JSX from lowercase HTML tags
        content = re.sub(
            r"<[A-Z][A-Za-z0-9.]*(?:\s[^>]*)?\s*/>", "", content, flags=re.DOTALL
        )

        # Unwrap block JSX — keep inner text, applied iteratively for nesting
        prev = None
        while prev != content:
            prev = content
            content = re.sub(
                r"<([A-Z][A-Za-z0-9.]*)(?:\s[^>]*)?>(.+?)</\1>",
                r"\2",
                content,
                flags=re.DOTALL,
            )

        # Strip JSX comments {/* ... */} and simple expressions {expr}
        content = re.sub(r"\{/\*.*?\*/\}", "", content, flags=re.DOTALL)
        content = re.sub(r"\{[^}]*\}", "", content)

        # Collapse excessive blank lines
        content = re.sub(r"\n{3,}", "\n\n", content)

        # Prepend frontmatter title/description as readable header
        header = ""
        title = metadata.get("title", "") if metadata else ""
        description = metadata.get("description", "") if metadata else ""
        if title:
            header = f"# {title}\n\n"
        if description:
            header += f"{description}\n\n"

        return header + content.strip()

    # ── Internal helpers ──────────────────────────────────────────────

    def _all_files(self) -> list[Path]:
        files: list[Path] = []
        for root in self._roots:
            for ext in self.EXTENSIONS:
                files.extend(root.rglob(f"*{ext}"))
        return files

    def _read(self, f: Path) -> str:
        text = f.read_text(encoding="utf-8", errors="ignore")
        if f.suffix == ".mdx":
            return self.mdx_to_md(text)
        return text

    # ── Public API ────────────────────────────────────────────────────

    def search(self, query: str, limit: int = 10) -> list[dict[str, Any]]:
        """Search docs by keyword in filename and first 500 chars of content."""
        q = query.lower()
        results: list[dict[str, Any]] = []
        for f in self._all_files():
            score = 0
            if q in f.stem.lower():
                score += 2
            try:
                head = self._read(f)[:500]
                if q in head.lower():
                    score += 1
            except OSError:
                continue
            if score:
                results.append({"score": score, "path": str(f), "excerpt": head[:200]})
        results.sort(key=lambda x: -x["score"])
        return results[:limit]

    def get(self, path: str) -> str:
        """Read a doc file by absolute path. MDX files are auto-converted to MD."""
        p = Path(path)
        if not p.exists():
            return f"File not found: {path}"
        return self._read(p)
