import re
import tree_sitter_cpp as tscpp

from swesmith.constants import TODO_REWRITE, CodeEntity, CodeProperty
from tree_sitter import Language, Parser, Query, QueryCursor
from swesmith.bug_gen.adapters.utils import build_entity

CPP_LANGUAGE = Language(tscpp.language())


class CPlusPlusEntity(CodeEntity):
    def _analyze_properties(self):
        """Analyze the code entity and add appropriate tags."""

        # Walk the tree to find patterns
        def walk_tree(node):
            """Recursively walk the tree to analyze properties."""
            # Check for function
            if node.type == "function_definition":
                self._tags.add(CodeProperty.IS_FUNCTION)
            # Check for class
            elif node.type == "class_specifier":
                self._tags.add(CodeProperty.IS_CLASS)

            # Control flow
            if node.type in [
                "for_statement",
                "while_statement",
                "do_statement",
                "for_range_loop",
            ]:
                self._tags.add(CodeProperty.HAS_LOOP)
            if node.type == "if_statement":
                self._tags.add(CodeProperty.HAS_IF)
                # Check for else clause
                for child in node.children:
                    if child.type == "else_clause":
                        self._tags.add(CodeProperty.HAS_IF_ELSE)
            if node.type == "switch_statement":
                self._tags.add(CodeProperty.HAS_SWITCH)

            # Operations
            if node.type == "binary_expression":
                self._tags.add(CodeProperty.HAS_BINARY_OP)
                # Check for arithmetic operations
                for child in node.children:
                    if child.type in ["+", "-", "*", "/", "%"]:
                        self._tags.add(CodeProperty.HAS_ARITHMETIC)
                # Check for comparison (potential off-by-one)
                for child in node.children:
                    if child.type in ["<", ">", "<=", ">="]:
                        self._tags.add(CodeProperty.HAS_OFF_BY_ONE)
                # Check for boolean ops
                for child in node.children:
                    if child.type in ["&&", "||"]:
                        self._tags.add(CodeProperty.HAS_BOOL_OP)

            if node.type == "unary_expression":
                self._tags.add(CodeProperty.HAS_UNARY_OP)

            if node.type == "call_expression":
                self._tags.add(CodeProperty.HAS_FUNCTION_CALL)

            if node.type == "return_statement":
                self._tags.add(CodeProperty.HAS_RETURN)

            if node.type == "assignment_expression":
                self._tags.add(CodeProperty.HAS_ASSIGNMENT)

            if node.type == "init_declarator":
                for child in node.children:
                    if child.type == "=":
                        self._tags.add(CodeProperty.HAS_ASSIGNMENT)

            # Recurse into children
            for child in node.children:
                walk_tree(child)

        walk_tree(self.node)

    @property
    def complexity(self) -> int:
        """Calculate the cyclomatic complexity of the function."""
        complexity = 1  # Base complexity

        def walk(node):
            nonlocal complexity
            # Decision points
            if node.type in [
                "if_statement",
                "while_statement",
                "for_statement",
                "do_statement",
                "for_range_loop",
            ]:
                complexity += 1
            # Exception handling
            elif node.type == "try_statement":
                complexity += len(
                    [c for c in node.children if c.type == "catch_clause"]
                )
            # Switch statements
            elif node.type == "case_statement":
                complexity += 1

            # Recurse
            for child in node.children:
                walk(child)

        walk(self.node)
        return complexity

    @property
    def name(self) -> str:
        func_query = Query(
            CPP_LANGUAGE,
            """
            [
                (function_definition (function_declarator declarator: (identifier) @name))
                (function_definition (function_declarator declarator: (destructor_name (identifier) @name)))
            ]
            """,
        )
        matches = QueryCursor(func_query).matches(self.node)
        if matches:
            name_node = matches[0][1]["name"][0]
            func_name = name_node.text.decode("utf-8")
            if name_node.parent.type == "destructor_name":
                return f"{func_name} Destructor"
            return func_name
        return ""

    @property
    def signature(self) -> str:
        body_query = Query(
            CPP_LANGUAGE, "(function_definition body: (compound_statement) @body)"
        )
        matches = QueryCursor(body_query).matches(self.node)
        if matches:
            body_node = matches[0][1]["body"][0]
            body_start_byte = body_node.start_byte - self.node.start_byte
            signature = self.node.text[:body_start_byte].strip().decode("utf-8")
            signature = re.sub(r"\(\s+", "(", signature).strip()
            signature = re.sub(r"\s+\)", ")", signature).strip()
            signature = re.sub(r"\s+", " ", signature).strip()
            return signature
        return ""

    @property
    def stub(self) -> str:
        return f"{self.signature} {{\n\t// {TODO_REWRITE}\n}}"


def get_entities_from_file_cpp(
    entities: list[CPlusPlusEntity],
    file_path: str,
    max_entities: int = -1,
) -> None:
    """
    Parse a .cpp file and return up to max_entities top-level funcs.
    If max_entities < 0, collects them all.
    """
    parser = Parser(CPP_LANGUAGE)

    file_content = open(file_path, "r", encoding="utf8").read()
    tree = parser.parse(bytes(file_content, "utf8"))
    root = tree.root_node
    lines = file_content.splitlines()

    def walk(node) -> None:
        # stop if we've hit the limit
        if 0 <= max_entities == len(entities):
            return

        # not checking for error nodes here because tree-sitter-cpp frequently
        # generates them parsing valid pre-processor directives

        if node.type == "function_definition":
            entities.append(build_entity(node, lines, file_path, CPlusPlusEntity))
            if 0 <= max_entities == len(entities):
                return

        for child in node.children:
            walk(child)

    walk(root)
