"""Python language parser using tree-sitter."""

from __future__ import annotations

from typing import Any

import tree_sitter_python
from tree_sitter import Node

from viper.languages.base import Function, TreeSitterParser


class PythonTreeSitterParser(TreeSitterParser):
    """Tree-sitter based parser for the Python language."""

    language_name = "python"
    file_extensions = [".py", ".pyw"]

    @staticmethod
    def _get_ts_language() -> Any:
        return tree_sitter_python.language()

    @staticmethod
    def _get_function_query() -> str:
        return "(function_definition) @func"

    def _extract_function(self, node: Node, code: bytes) -> Function:
        func = Function()
        func.start = node.start_point[0] + 1
        func.end = node.end_point[0] + 1

        # Name + params (always present)
        name_params_q = """
        (function_definition
            name: (identifier) @name
            parameters: (parameters) @params
        )
        """
        for key, nodes in self.capture_nodes(node, name_params_q).items():
            a = nodes[0]
            val = code[a.start_byte:a.end_byte].decode("utf-8")
            if key == "name":
                func.name = val
                func.line = a.start_point[0] + 1
            elif key == "params":
                func.signature = val

        # Return-type annotation (optional)
        ret_q = """
        (function_definition
            return_type: (type) @return_type
        )
        """
        for key, nodes in self.capture_nodes(node, ret_q).items():
            if key == "return_type":
                a = nodes[0]
                func.return_type = code[a.start_byte:a.end_byte].decode("utf-8")

        return func

    def _get_decision_node_types(self) -> set[str]:
        return {
            "if_statement",
            "for_statement",
            "while_statement",
            "except_clause",
            "elif_clause",
            "conditional_expression",  # ternary: x if cond else y
            "boolean_operator",  # 'and' / 'or'
        }

    def _get_modified_decision_node_types(self) -> set[str]:
        return {
            "if_statement",
            "for_statement",
            "while_statement",
            "except_clause",
            "elif_clause",
            "match_statement",
            "conditional_expression",
            "boolean_operator",
        }

    def _get_statement_node_types(self) -> set[str]:
        return {
            "expression_statement",
            "return_statement",
            "if_statement",
            "for_statement",
            "while_statement",
            "try_statement",
            "with_statement",
            "raise_statement",
            "assert_statement",
            "pass_statement",
            "break_statement",
            "continue_statement",
            "import_statement",
            "import_from_statement",
            "match_statement",
            "delete_statement",
        }

    def _get_scope_name(self, node: Node) -> str | None:
        """Extract class or nested function scope names from ancestor nodes."""
        if node.type == "class_definition":
            for child in node.children:
                if child.type == "identifier":
                    return child.text.decode("utf-8") if child.text else None
        if node.type == "function_definition":
            for child in node.children:
                if child.type == "identifier":
                    return child.text.decode("utf-8") if child.text else None
        return None

    def _compute_fqdn(self, node: Node, name: str) -> str:
        """Compute FQDN with ``.`` separator (Python style: ``Class.method``)."""
        parts: list[str] = []
        current = node.parent
        while current is not None:
            part = self._get_scope_name(current)
            if part:
                parts.append(part)
            current = current.parent
        parts.reverse()
        parts.append(name)
        return ".".join(parts) if len(parts) > 1 else name
