"""Cognitive complexity for Go (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
from vibelexity.parsers.go import is_named_func_literal

_NESTING_CONTAINERS = {
    "function_declaration",
    "method_declaration",
    "func_literal",
}

# Control flow nodes that add 1 + nesting and then recurse at nesting + 1
_GO_NESTING_INCREMENTORS = {
    "for_statement",
    "expression_switch_statement",
    "type_switch_statement",
    "select_statement",
}


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 child.type == "func_literal" and not is_named_func_literal(child):
                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 _GO_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(node: Node, nesting: int, func_name: str, top_func: Node) -> int:
    """Walk children of a Go if_statement."""
    total = 0
    consequence = node.child_by_field_name("consequence")
    alternative = node.child_by_field_name("alternative")

    for child in node.children:
        if consequence is not None and child == consequence:
            total += _walk(child, nesting + 1, func_name, top_func)
        elif alternative is not None and child == alternative:
            if child.type == "if_statement":
                total += 1
                total += _walk_if(child, nesting, func_name, top_func)
            elif child.type == "block":
                total += 1
                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)
        elif child.type == "call_expression":
            total += _check_recursion(child, func_name)
            total += _walk(child, nesting, func_name, top_func)
        else:
            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 == "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
    callee = call_node.child_by_field_name("function")
    if callee and callee.type == "identifier" and callee.text.decode() == func_name:
        return 1
    return 0


compute_cognitive_go = make_compute_cognitive(_walk)
