"""
Tree-sitter parser service.

Provides multi-language code parsing using Tree-sitter.
"""

import logging
from pathlib import Path
from typing import TYPE_CHECKING

from tree_sitter import Language, Parser, Tree, Node

from .types import (
    ParseResult,
    ExtractedFunction,
    ExtractedClass,
    ExtractedCall,
    ExtractedImport,
)

if TYPE_CHECKING:
    pass

logger = logging.getLogger(__name__)


class ParserService:
    """Service for parsing source code using Tree-sitter."""

    def __init__(self):
        self._parsers: dict[str, Parser] = {}
        self._languages: dict[str, Language] = {}

    def _get_language(self, language: str) -> Language:
        """Get or initialize a Tree-sitter Language."""
        if language not in self._languages:
            if language == "python":
                import tree_sitter_python as ts_python

                self._languages[language] = Language(ts_python.language())
            elif language == "javascript":
                import tree_sitter_javascript as ts_javascript

                self._languages[language] = Language(ts_javascript.language())
            elif language == "typescript":
                import tree_sitter_typescript as ts_typescript

                self._languages[language] = Language(
                    ts_typescript.language_typescript()
                )
            elif language == "tsx":
                import tree_sitter_typescript as ts_typescript

                self._languages[language] = Language(ts_typescript.language_tsx())
            else:
                raise ValueError(f"Unsupported language: {language}")

        return self._languages[language]

    def _get_parser(self, language: str) -> Parser:
        """Get or initialize a parser for a language."""
        if language not in self._parsers:
            lang = self._get_language(language)
            parser = Parser(lang)
            self._parsers[language] = parser

        return self._parsers[language]

    def parse(self, source: str | bytes, language: str) -> Tree:
        """
        Parse source code into an AST.

        Args:
            source: Source code as string or bytes
            language: Language name (python, javascript, typescript, tsx)

        Returns:
            Tree-sitter Tree object
        """
        if isinstance(source, str):
            source = source.encode("utf-8")

        parser = self._get_parser(language)
        tree = parser.parse(source)
        return tree

    def get_node_text(self, node: Node, source: bytes) -> str:
        """Extract text for a node from source."""
        return source[node.start_byte : node.end_byte].decode("utf-8")

    def find_nodes_by_type(
        self, root: Node, node_types: list[str], recursive: bool = True
    ) -> list[Node]:
        """
        Find all nodes of specified types.

        Args:
            root: Root node to search from
            node_types: List of node type strings to match
            recursive: Whether to search recursively

        Returns:
            List of matching nodes
        """
        results: list[Node] = []
        stack: list[Node] = [root]

        while stack:
            node = stack.pop()
            if node.type in node_types:
                results.append(node)

            if recursive:
                stack.extend(reversed(node.children))

        return results

    def find_child_by_field(self, node: Node, field_name: str) -> Node | None:
        """Find child node by field name."""
        return node.child_by_field_name(field_name)

    def find_children_by_type(self, node: Node, node_type: str) -> list[Node]:
        """Find direct children of a specific type."""
        return [child for child in node.children if child.type == node_type]

    def parse_file(self, file_path: str, content: str, language: str) -> ParseResult:
        """
        Parse a file and extract all code elements.

        Args:
            file_path: Path to the file (for qualified names)
            content: Source code content
            language: Language name

        Returns:
            ParseResult with extracted elements
        """
        try:
            source_bytes = content.encode("utf-8")
            tree = self.parse(source_bytes, language)
            root = tree.root_node

            # Create module qualified name from file path
            module_qn = self._path_to_module_qn(file_path)

            # Get the appropriate extractor
            if language == "python":
                from .python_extractor import PythonExtractor

                extractor = PythonExtractor(source_bytes, module_qn)
            elif language in ("javascript", "typescript", "tsx"):
                from .js_extractor import JavaScriptExtractor

                is_ts = language in ("typescript", "tsx")
                extractor = JavaScriptExtractor(source_bytes, module_qn, is_ts)
            else:
                return ParseResult(
                    file_path=file_path,
                    language=language,
                    error=f"Unsupported language: {language}",
                )

            # Extract elements
            functions = extractor.extract_functions(root)
            classes = extractor.extract_classes(root)
            imports = extractor.extract_imports(root)

            # Extract calls from functions and methods
            calls: list[ExtractedCall] = []
            for func in functions:
                if func.node:
                    calls.extend(extractor.extract_calls(func.node, func.qualified_name))
            for cls in classes:
                for method in cls.methods:
                    if method.node:
                        calls.extend(
                            extractor.extract_calls(method.node, method.qualified_name)
                        )

            return ParseResult(
                file_path=file_path,
                language=language,
                functions=functions,
                classes=classes,
                imports=imports,
                calls=calls,
            )

        except Exception as e:
            logger.exception(f"Error parsing {file_path}")
            return ParseResult(
                file_path=file_path,
                language=language,
                error=str(e),
            )

    def _path_to_module_qn(self, file_path: str) -> str:
        """Convert file path to module qualified name."""
        path = Path(file_path)
        # Remove extension
        stem = path.stem
        # Get parent dirs up to src or root
        parts = list(path.parts[:-1]) + [stem]

        # Try to find a common root like 'src'
        try:
            src_idx = parts.index("src")
            parts = parts[src_idx + 1 :]
        except ValueError:
            pass

        # Remove __init__ from the end
        if parts and parts[-1] == "__init__":
            parts = parts[:-1]

        return ".".join(parts) if parts else stem


# Global singleton instance
parser_service = ParserService()
