"""TypeScript dead code parser."""

from __future__ import annotations

from tree_sitter import Node

from vibelexity.parsers.typescript import TS_LANGUAGE
from vibelexity.patterns.common import create_query, run_captures
from vibelexity.dead_code.parsers.base import (
    SymbolDefinition,
    ImportedSymbol,
    DeadCodeParser,
)


class TypeScriptDeadCodeParser(DeadCodeParser):
    def _get_language(self):
        return TS_LANGUAGE

    def extract_imports(self, root: Node, source: str) -> list[ImportedSymbol]:
        imports = []
        query_str = "(import_statement) @import_stmt"
        query = create_query(TS_LANGUAGE, query_str)
        captures = run_captures(query, root)

        for stmt_node, _ in captures:
            self._process_import_statement(stmt_node, imports)
        return imports

    def _process_import_statement(
        self, stmt_node: Node, imports: list[ImportedSymbol]
    ) -> None:
        for child in stmt_node.children:
            if child.type == "import" and len(stmt_node.children) > 1:
                next_child = stmt_node.children[1]
                if (
                    next_child.type == "type"
                    and next_child.text
                    and next_child.text.decode() == "type"
                ):
                    return
            if child.type == "import_clause":
                self._process_import_clause(child, imports)
                break

    def _process_import_clause(
        self, clause_node: Node, imports: list[ImportedSymbol]
    ) -> None:
        for child in clause_node.children:
            if child.type == "named_imports":
                self._process_named_imports(child, imports)
            elif child.type == "namespace_import":
                self._process_namespace_import(child, imports)
            elif child.type == "identifier":
                name = child.text.decode() if child.text else ""
                if name and name[0].isupper():
                    imports.append(
                        ImportedSymbol(name, child.start_point[0] + 1, None, None)
                    )

    def _process_named_imports(
        self, named_imports: Node, imports: list[ImportedSymbol]
    ) -> None:
        for child in named_imports.children:
            if child.type == "import_specifier":
                for nested in child.children:
                    if nested.type == "identifier":
                        name = nested.text.decode() if nested.text else ""
                        if name:
                            imports.append(
                                ImportedSymbol(
                                    name, nested.start_point[0] + 1, None, None
                                )
                            )

    def _process_namespace_import(
        self, namespace_node: Node, imports: list[ImportedSymbol]
    ) -> None:
        for child in namespace_node.children:
            if child.type == "identifier":
                name = child.text.decode() if child.text else ""
                if name:
                    imports.append(
                        ImportedSymbol(name, child.start_point[0] + 1, None, None)
                    )

    def extract_definitions(self, root: Node, source: str) -> list[SymbolDefinition]:
        defs = []
        query_configs = [
            ("(function_declaration) @func_decl", "identifier", "function"),
            ("(generator_function_declaration) @func_decl", "identifier", "function"),
            ("(class_declaration) @class_decl", "type_identifier", "class"),
            ("(method_definition) @method_def", "property_identifier", "function"),
        ]

        for query_str, child_type, def_type in query_configs:
            try:
                query = create_query(TS_LANGUAGE, query_str)
                captures = run_captures(query, root)
                for node, _ in captures:
                    for child in node.children:
                        if child.type == child_type:
                            text = child.text.decode() if child.text else ""
                            defs.append(
                                SymbolDefinition(
                                    text,
                                    node.start_point[0] + 1,
                                    def_type,
                                    self.is_exported(node),
                                )
                            )
                            break
            except Exception:
                pass

        query_str = "(lexical_declaration (variable_declarator name: (identifier) @name value: (arrow_function)))"
        try:
            query = create_query(TS_LANGUAGE, query_str)
            captures = run_captures(query, root)
            for node, capture_name in captures:
                text = node.text.decode() if node.text else ""
                if capture_name == "name":
                    lexical_parent = node.parent.parent if node.parent else None
                    is_exported = (
                        self.is_exported(lexical_parent) if lexical_parent else False
                    )
                    defs.append(
                        SymbolDefinition(
                            text, node.start_point[0] + 1, "function", is_exported
                        )
                    )
        except Exception:
            pass

        return defs

    def extract_abstract_info(
        self, root: Node, source: str
    ) -> tuple[set[str], set[str]]:
        abstract_classes = set()
        abstract_methods = set()

        query_str = "(abstract_class_declaration name: (type_identifier) @class_name)"
        try:
            query = create_query(TS_LANGUAGE, query_str)
            captures = run_captures(query, root)
            for node, capture_name in captures:
                text = node.text.decode() if node.text else ""
                if capture_name == "class_name":
                    abstract_classes.add(text)
        except Exception:
            pass

        query_str = (
            "(abstract_method_signature name: (property_identifier) @method_name)"
        )
        try:
            query = create_query(TS_LANGUAGE, query_str)
            captures = run_captures(query, root)
            for node, capture_name in captures:
                text = node.text.decode() if node.text else ""
                if capture_name == "method_name":
                    abstract_methods.add(text)
        except Exception:
            pass

        return abstract_classes, abstract_methods

    def is_exported(self, node: Node) -> bool:
        parent = node.parent
        while parent:
            if parent.type == "export_statement":
                return True
            parent = parent.parent
        return False
