"""NPath complexity for Go."""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from tree_sitter import Node

from vibelexity.common import _count_bool_ops
from vibelexity.parsers.go import is_named_func_literal


def compute_npath_go(func_node: Node) -> int:
    """Compute NPath complexity for a Go function node."""
    body = func_node.child_by_field_name("body")
    if body and body.type == "block":
        return _npath_block_go(body, func_node)
    for child in func_node.children:
        if child.type == "block":
            return _npath_block_go(child, func_node)
    return 1


def _npath_block_go(block: Node, top_func: Node) -> int:
    """Multiply NPath of each child statement in a block."""
    result = 1
    for child in block.children:
        if child.type == "statement_list":
            for stmt in child.children:
                result *= _npath_stmt_go(stmt, top_func)
        elif child.type not in ("{", "}"):
            result *= _npath_stmt_go(child, top_func)
    return result


def _npath_stmt_go(node: Node, top_func: Node) -> int:
    """Dispatch a statement node to its NPath handler."""
    # Handle function-like nodes that need special treatment
    if node.type == "func_literal" and node is not top_func:
        return compute_npath_go(node) if not is_named_func_literal(node) else 1

    if (
        node.type in ("function_declaration", "method_declaration")
        and node is not top_func
    ):
        return 1

    # Dispatch handlers
    handlers = {
        "if_statement": _npath_if_go,
        "for_statement": _npath_loop_go,
        "expression_switch_statement": _npath_switch_go,
        "type_switch_statement": _npath_switch_go,
        "select_statement": _npath_select_go,
        "block": _npath_block_go,
    }
    handler = handlers.get(node.type)
    if handler:
        return handler(node, top_func)

    # Expression statements
    expr_stmt_types = {
        "expression_statement",
        "return_statement",
        "short_var_declaration",
        "assignment_statement",
        "go_statement",
        "defer_statement",
        "send_statement",
    }
    if node.type in expr_stmt_types:
        return _npath_expr_go(node, top_func)

    return 1


def _npath_if_go(node: Node, top_func: Node) -> int:
    """NPath for Go if_statement using consequence/alternative fields."""
    total = 0
    has_else = False

    condition = node.child_by_field_name("condition")
    bool_ops = _count_bool_ops(condition) if condition else 0

    consequence = node.child_by_field_name("consequence")
    if consequence and consequence.type == "block":
        total += _npath_block_go(consequence, top_func)

    alternative = node.child_by_field_name("alternative")
    if alternative is not None:
        has_else = True
        if alternative.type == "if_statement":
            total += _npath_if_go(alternative, top_func)
        elif alternative.type == "block":
            total += _npath_block_go(alternative, top_func)

    if not has_else:
        total += 1

    total += bool_ops
    return total


def _npath_loop_go(node: Node, top_func: Node) -> int:
    """NPath for Go for_statement (covers for, for-range, infinite loop)."""
    body_npath = 1
    bool_ops = 0

    body = node.child_by_field_name("body")
    if body and body.type == "block":
        body_npath = _npath_block_go(body, top_func)

    condition = node.child_by_field_name("condition")
    if condition:
        bool_ops = _count_bool_ops(condition)

    return body_npath + 1 + bool_ops


def _npath_switch_go(node: Node, top_func: Node) -> int:
    """NPath for expression_switch_statement / type_switch_statement."""
    total = 0
    has_default = False
    for child in node.children:
        if child.type in ("expression_case", "type_case"):
            case_npath = 1
            for sub in child.children:
                if sub.type in ("expression_list", "type_list", "case", "default", ":"):
                    continue
                sub_np = _npath_stmt_go(sub, top_func)
                if sub_np > 1:
                    case_npath *= sub_np
            total += case_npath
        elif child.type == "default_case":
            has_default = True
            case_npath = 1
            for sub in child.children:
                if sub.type in ("default", ":"):
                    continue
                sub_np = _npath_stmt_go(sub, top_func)
                if sub_np > 1:
                    case_npath *= sub_np
            total += case_npath
    if not has_default:
        total += 1
    return max(total, 1)


def _npath_select_go(node: Node, top_func: Node) -> int:
    """NPath for select_statement with communication_case / default_case."""
    total = 0
    has_default = False
    for child in node.children:
        if child.type == "communication_case":
            case_npath = 1
            for sub in child.children:
                if sub.type in ("case", "default", ":"):
                    continue
                sub_np = _npath_stmt_go(sub, top_func)
                if sub_np > 1:
                    case_npath *= sub_np
            total += case_npath
        elif child.type == "default_case":
            has_default = True
            case_npath = 1
            for sub in child.children:
                if sub.type in ("default", ":"):
                    continue
                sub_np = _npath_stmt_go(sub, top_func)
                if sub_np > 1:
                    case_npath *= sub_np
            total += case_npath
    if not has_default:
        total += 1
    return max(total, 1)


def _npath_expr_go(node: Node, top_func: Node) -> int:
    """Scan expression for inline func_literals, multiplying results."""
    if node.type == "func_literal" and node is not top_func:
        if is_named_func_literal(node):
            return 1
        return compute_npath_go(node)

    result = 1
    for child in node.children:
        child_np = _npath_expr_go(child, top_func)
        if child_np > 1:
            result *= child_np
    return result
