"""Python API document adapter for PFC search system.

This module converts Python SDK API documentation from the DocumentationLoader
format into unified SearchDocument models for search.
"""

from typing import List
from pfc_mcp.docs.python_api.loader import DocumentationLoader
from pfc_mcp.docs.models.document import SearchDocument, DocumentType


class APIDocumentAdapter:
    """Adapter for Python API documentation.

    Converts Python SDK API data from DocumentationLoader into unified
    SearchDocument format. This enables:
    - Independent search for Python APIs
    - Consistent interface with command search
    - Separation of data loading and search logic

    Design:
    - Only function/method APIs (not module-level docs)
    - DocumentType.PYTHON_API for all entries
    - Metadata preserves signature and parameter info

    Usage:
        >>> # Load all API documents
        >>> documents = APIDocumentAdapter.load_all()
        >>> len(documents)
        100  # Approximate count

        >>> # Example document
        >>> doc = documents[0]
        >>> doc.name
        'itasca.ball.create'
        >>> doc.doc_type
        <DocumentType.PYTHON_API: 'python_api'>
    """

    @staticmethod
    def load_all() -> List[SearchDocument]:
        """Load all Python API documents.

        Returns:
            List of SearchDocument instances for all APIs

        Note:
            This loads function/method APIs only, not module-level docs.
            Module queries are handled separately by DocumentationLoader.

        Example:
            >>> docs = APIDocumentAdapter.load_all()
            >>> api_doc = docs[0]
            >>> api_doc.name
            'itasca.ball.create'
            >>> api_doc.syntax
            'itasca.ball.create(radius, pos=None)'
        """
        documents = []
        index = DocumentationLoader.load_index()
        quick_ref = index.get("quick_ref", {})

        # Iterate through all API entries in quick_ref
        for api_name, file_ref in quick_ref.items():
            # Load the API documentation
            api_doc = DocumentationLoader.load_api_doc(api_name)

            if not api_doc:
                continue

            # Skip module-level docs (they have "type": "module")
            if api_doc.get("type") == "module":
                continue

            # Extract category from API name
            # Examples:
            # - "itasca.ball.create" → category = "itasca.ball"
            # - "itasca.ball.Ball.vel" → category = "itasca.ball"
            # - "itasca.BallBallContact.gap" → category = "itasca.contact"
            category = APIDocumentAdapter._extract_category(api_name)

            # Build description from API doc
            description = api_doc.get("description", "")

            # Add parameter information to description for better search
            parameters = api_doc.get("parameters", [])
            if parameters:
                param_names = [p.get("name", "") for p in parameters]
                description += f"\n\nParameters: {', '.join(param_names)}"

            # Get keywords from the keywords index
            all_keywords = DocumentationLoader.load_all_keywords()
            keywords = []
            for keyword, api_list in all_keywords.items():
                if api_name in api_list:
                    keywords.append(keyword)

            # Convert to SearchDocument
            doc = SearchDocument(
                name=api_name,
                doc_type=DocumentType.PYTHON_API,
                title=api_name,
                description=description,
                keywords=keywords,
                category=category,
                syntax=api_doc.get("signature", api_name),
                examples=[
                    {"code": ex} if isinstance(ex, str) else ex
                    for ex in api_doc.get("examples", [])
                ],
                metadata={
                    "file_ref": file_ref,
                    "returns": api_doc.get("returns", ""),
                    "limitations": api_doc.get("limitations", []),
                    "fallback_commands": api_doc.get("fallback_commands", []),
                    "see_also": api_doc.get("see_also", [])
                }
            )
            documents.append(doc)

        return documents

    @staticmethod
    def _extract_category(api_name: str) -> str:
        """Extract category from API name.

        Args:
            api_name: Full API path (e.g., "itasca.ball.create", "Ball.vel")

        Returns:
            Category string (e.g., "itasca.ball", "itasca.contact")

        Examples:
            >>> APIDocumentAdapter._extract_category("itasca.ball.create")
            'itasca.ball'
            >>> APIDocumentAdapter._extract_category("itasca.ball.Ball.vel")
            'itasca.ball'
            >>> APIDocumentAdapter._extract_category("itasca.BallBallContact.gap")
            'itasca.contact'
        """
        # Import contact types for mapping
        try:
            from pfc_mcp.docs.python_api.types.contact import CONTACT_TYPES
        except ImportError:
            CONTACT_TYPES = []

        # Check if it's a Contact type (e.g., BallBallContact, BallFacetContact)
        parts = api_name.split(".")
        if len(parts) >= 2:
            # Check if second part is a Contact type
            if parts[1] in CONTACT_TYPES:
                return "itasca.contact"

        # Standard module function: itasca.ball.create → itasca.ball
        if api_name.startswith("itasca.") and len(parts) >= 3:
            return ".".join(parts[:2])  # itasca.ball, itasca.wall, etc.

        # Object method: Ball.vel → itasca.ball
        # (This should rarely happen after index expansion, but handle it)
        if len(parts) == 2:
            # Try to map class name to module
            try:
                from pfc_mcp.docs.python_api.types.mappings import CLASS_TO_MODULE
                class_name = parts[0]
                if class_name in CLASS_TO_MODULE:
                    return f"itasca.{CLASS_TO_MODULE[class_name]}"
            except ImportError:
                pass

        # Fallback: use first two parts or first part
        if len(parts) >= 2:
            return ".".join(parts[:2])
        return parts[0] if parts else "unknown"

    @staticmethod
    def load_by_id(doc_id: str) -> SearchDocument:
        """Load a specific API document by ID.

        Args:
            doc_id: API name (e.g., "itasca.ball.create", "Ball.vel")

        Returns:
            SearchDocument instance or None if not found

        Example:
            >>> doc = APIDocumentAdapter.load_by_id("itasca.ball.create")
            >>> doc.title
            'itasca.ball.create'
            >>> doc.syntax
            'itasca.ball.create(radius, pos=None)'
        """
        api_doc = DocumentationLoader.load_api_doc(doc_id)

        if not api_doc or api_doc.get("type") == "module":
            return None

        # Extract category
        category = APIDocumentAdapter._extract_category(doc_id)

        # Build description
        description = api_doc.get("description", "")
        parameters = api_doc.get("parameters", [])
        if parameters:
            param_names = [p.get("name", "") for p in parameters]
            description += f"\n\nParameters: {', '.join(param_names)}"

        # Get keywords
        all_keywords = DocumentationLoader.load_all_keywords()
        keywords = []
        for keyword, api_list in all_keywords.items():
            if doc_id in api_list:
                keywords.append(keyword)

        return SearchDocument(
            name=doc_id,
            doc_type=DocumentType.PYTHON_API,
            title=doc_id,
            description=description,
            keywords=keywords,
            category=category,
            syntax=api_doc.get("signature", doc_id),
            examples=[
                {"code": ex} if isinstance(ex, str) else ex
                for ex in api_doc.get("examples", [])
            ],
            metadata={
                "returns": api_doc.get("returns", ""),
                "limitations": api_doc.get("limitations", []),
                "see_also": api_doc.get("see_also", [])
            }
        )
