"""Cognitive complexity for TypeScript (SonarSource spec)."""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from tree_sitter import Node

from vibelexity.common import _is_logical_binary, _score_boolean_chain
from vibelexity.metrics.cognitive.common import make_compute_cognitive

_NESTING_CONTAINERS = {
    "function_declaration",
    "function_expression",
    "method_definition",
    "arrow_function",
    "generator_function_declaration",
}

# Control flow nodes that add 1 + nesting and then recurse at nesting + 1
_TS_NESTING_INCREMENTORS = {
    "for_statement",
    "for_in_statement",
    "while_statement",
    "do_statement",
    "catch_clause",
    "ternary_expression",
    "switch_statement",
}


def _is_skip_container(child: Node, top_func: Node) -> bool:
    """Check if a child is a nested function container that should be skipped."""
    if child.type not in _NESTING_CONTAINERS or child is top_func:
        return False
    if child.type == "arrow_function":
        return bool(child.parent and child.parent.type == "variable_declarator")
    return True


def _walk(node: Node, nesting: int, func_name: str, top_func: Node) -> int:
    """Recursively walk AST and compute cognitive complexity."""
    total = 0

    for child in node.children:
        if child.type in _NESTING_CONTAINERS and child is not top_func:
            if not _is_skip_container(child, top_func):
                total += _walk(child, nesting + 1, func_name, top_func)
            continue

        if child.type == "if_statement":
            total += 1 + nesting
            total += _walk_if(child, nesting, func_name, top_func)
            continue

        if child.type in _TS_NESTING_INCREMENTORS:
            total += 1 + nesting
            total += _walk(child, nesting + 1, func_name, top_func)
            continue

        if _is_logical_binary(child):
            total += _score_boolean_chain(child)
            total += _walk_non_boolean(child, nesting, func_name, top_func)
            continue

        if child.type == "call_expression":
            total += _check_recursion(child, func_name)

        total += _walk(child, nesting, func_name, top_func)

    return total


def _walk_if_else(child: Node, nesting: int, func_name: str, top_func: Node) -> int:
    """Handle an else_clause within an if_statement."""
    inner_ifs = [c for c in child.children if c.type == "if_statement"]
    if inner_ifs:
        return 1 + _walk_if(inner_ifs[0], nesting, func_name, top_func)
    return 1 + _walk(child, nesting + 1, func_name, top_func)


def _walk_if(node: Node, nesting: int, func_name: str, top_func: Node) -> int:
    """Walk children of an if_statement."""
    total = 0
    for child in node.children:
        if child.type == "else_clause":
            total += _walk_if_else(child, nesting, func_name, top_func)
        elif child.type == "statement_block":
            total += _walk(child, nesting + 1, func_name, top_func)
        elif _is_logical_binary(child):
            total += _score_boolean_chain(child)
            total += _walk_non_boolean(child, nesting, func_name, top_func)
        else:
            if child.type == "call_expression":
                total += _check_recursion(child, func_name)
            total += _walk(child, nesting, func_name, top_func)
    return total


def _walk_non_boolean(node: Node, nesting: int, func_name: str, top_func: Node) -> int:
    """Walk inside boolean expressions looking only for nested structures and recursion."""
    total = 0
    for child in node.children:
        if _is_logical_binary(child):
            total += _walk_non_boolean(child, nesting, func_name, top_func)
            continue
        if child.type == "ternary_expression":
            total += 1 + nesting
            total += _walk(child, nesting + 1, func_name, top_func)
            continue
        if child.type == "call_expression":
            total += _check_recursion(child, func_name)
            total += _walk(child, nesting, func_name, top_func)
            continue
        total += _walk_non_boolean(child, nesting, func_name, top_func)
    return total


def _check_recursion(call_node: Node, func_name: str) -> int:
    """Check if a call_expression is a recursive call to the enclosing function."""
    if not func_name:
        return 0
    for child in call_node.children:
        if child.type == "identifier" and child.text.decode() == func_name:
            return 1
        if child.type == "arguments":
            break
    return 0


compute_cognitive_ts = make_compute_cognitive(_walk)
