"""ES6 and CommonJS import extraction for tree-sitter.

Extracts import statements from JavaScript and TypeScript parse trees.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from unfamiliar.analysis.adapters._treesitter_utils import get_node_text, traverse
from unfamiliar.analysis.models import ParsedImport

if TYPE_CHECKING:
    from tree_sitter import Node


def extract_es_imports(root: Node, source_path: str) -> list[ParsedImport]:
    """Extract ES module import statements from a tree-sitter parse tree.

    Handles:
    - import x from 'module'
    - import { a, b } from 'module'
    - import * as x from 'module'
    - import 'module' (side-effect imports)

    Args:
        root: The root node of the parsed JS/TS file.
        source_path: Path of the file being parsed (for ParsedImport.source_path).

    Returns:
        List of ParsedImport objects for each import statement.
    """
    imports: list[ParsedImport] = []

    for node in traverse(root):
        if node.type == "import_statement":
            import_info = _parse_es_import(node, source_path)
            if import_info:
                imports.append(import_info)

    return imports


def _parse_es_import(node: Node, source_path: str) -> ParsedImport | None:
    """Parse a single ES import statement node."""
    source_str = None
    names: list[str] = []

    # Find the source string (the module path)
    for child in node.children:
        if child.type == "string":
            source_str = _get_string_content(child)
            break

    if source_str is None:
        return None

    # Determine if relative
    is_relative = source_str.startswith(".")

    # Extract imported names from import_clause
    for child in node.children:
        if child.type == "import_clause":
            names = _extract_import_names(child)
            break

    return ParsedImport(
        source_path=source_path,
        target=source_str,
        is_relative=is_relative,
        names=names,
    )


def _extract_import_names(import_clause: Node) -> list[str]:
    """Extract names from an import_clause node."""
    names: list[str] = []

    for child in import_clause.children:
        if child.type == "identifier":
            # import x from 'module'
            names.append(get_node_text(child))
        elif child.type == "named_imports":
            # import { a, b } from 'module'
            for spec in child.children:
                if spec.type == "import_specifier":
                    name_node = spec.child_by_field_name("name")
                    if name_node:
                        names.append(get_node_text(name_node))
        elif child.type == "namespace_import":
            # import * as x from 'module'
            names.append("*")

    return names


def extract_require_imports(root: Node, source_path: str) -> list[ParsedImport]:
    """Extract CommonJS require() calls from a tree-sitter parse tree.

    Only captures require() calls with string literal arguments.
    Dynamic require(require(variable)) is not captured.

    Args:
        root: The root node of the parsed JS file.
        source_path: Path of the file being parsed.

    Returns:
        List of ParsedImport objects for each require() call.
    """
    imports: list[ParsedImport] = []

    for node in traverse(root):
        if node.type == "call_expression":
            func = node.child_by_field_name("function")
            if func and get_node_text(func) == "require":
                args = node.child_by_field_name("arguments")
                if args:
                    for arg in args.children:
                        if arg.type == "string":
                            source_str = _get_string_content(arg)
                            if source_str:
                                is_relative = source_str.startswith(".")
                                imports.append(
                                    ParsedImport(
                                        source_path=source_path,
                                        target=source_str,
                                        is_relative=is_relative,
                                        names=[],
                                    )
                                )
                            break

    return imports


def _get_string_content(node: Node) -> str | None:
    """Extract the content of a string literal node (without quotes)."""
    text = get_node_text(node)
    if len(text) >= 2 and text[0] in ('"', "'", "`"):
        return text[1:-1]
    return text if text else None
