"""Tests for Go cognitive complexity."""

from vibelexity.analyzers.go import analyze_source


def _score(code: str) -> int:
    """Helper: return total complexity of the first function in code."""
    result = analyze_source(code)
    assert result.functions, f"No functions found in:\n{code}"
    return result.functions[0].complexity


def _scores(code: str) -> dict[str, int]:
    """Helper: return {name: complexity} for all functions in code."""
    result = analyze_source(code)
    return {f.name: f.complexity for f in result.functions}


# --- Basic control flow ---


def test_empty_function():
    code = """
package main

func f() {}
"""
    assert _score(code) == 0


def test_single_if():
    code = """
package main

func f(x int) {
    if x > 0 {       // +1
    }
}
"""
    assert _score(code) == 1


def test_if_else():
    code = """
package main

func f(x int) {
    if x > 0 {       // +1
    } else {          // +1
    }
}
"""
    assert _score(code) == 2


def test_if_else_if_else():
    code = """
package main

func f(x int) {
    if x > 0 {            // +1
    } else if x < 0 {     // +1
    } else {               // +1
    }
}
"""
    assert _score(code) == 3


# --- Loops ---


def test_for_c_style():
    code = """
package main

func f(n int) {
    for i := 0; i < n; i++ {   // +1
    }
}
"""
    assert _score(code) == 1


def test_for_range():
    code = """
package main

func f(items []int) {
    for _, v := range items {   // +1
    }
}
"""
    assert _score(code) == 1


def test_for_as_while():
    code = """
package main

func f(x int) {
    for x > 0 {   // +1
        x--
    }
}
"""
    assert _score(code) == 1


def test_infinite_for():
    code = """
package main

func f() {
    for {   // +1
        break
    }
}
"""
    assert _score(code) == 1


# --- Nesting ---


def test_nested_if():
    code = """
package main

func f(a, b bool) {
    if a {           // +1 (nesting 0)
        if b {       // +1 + 1 (nesting 1)
        }
    }
}
"""
    assert _score(code) == 3


def test_deeply_nested():
    code = """
package main

func f(a bool, items []int, c bool) {
    if a {                        // +1 (nesting 0)
        for _, v := range items { // +1 + 1 (nesting 1)
            if c {                // +1 + 2 (nesting 2)
                _ = v
            }
        }
    }
}
"""
    assert _score(code) == 6


def test_if_else_nesting():
    code = """
package main

func f(a, b bool) {
    if a {           // +1 (nesting 0)
    } else {         // +1 (no nesting bonus)
        if b {       // +1 + 1 (nesting 1, inside else body)
        }
    }
}
"""
    assert _score(code) == 4


# --- Switch/Select ---


def test_expression_switch():
    code = """
package main

func f(x int) {
    switch x {       // +1 (nesting 0)
    case 1:
    case 2:
    default:
    }
}
"""
    assert _score(code) == 1


def test_type_switch():
    code = """
package main

func f(x interface{}) {
    switch x.(type) {   // +1 (nesting 0)
    case int:
    case string:
    }
}
"""
    assert _score(code) == 1


def test_select_statement():
    code = """
package main

func f(ch1, ch2 chan int) {
    select {           // +1 (nesting 0)
    case v := <-ch1:
        _ = v
    case v := <-ch2:
        _ = v
    }
}
"""
    assert _score(code) == 1


def test_switch_nested_in_if():
    code = """
package main

func f(x int, flag bool) {
    if flag {            // +1 (nesting 0)
        switch x {       // +1 + 1 (nesting 1)
        case 1:
        case 2:
        }
    }
}
"""
    assert _score(code) == 3


# --- Boolean operators ---


def test_single_and():
    code = """
package main

func f(a, b bool) {
    if a && b {   // +1 (if) + 1 (boolean)
    }
}
"""
    assert _score(code) == 2


def test_chained_same_op():
    code = """
package main

func f(a, b, c bool) {
    if a && b && c {   // +1 (if) + 1 (boolean chain, same op)
    }
}
"""
    assert _score(code) == 2


def test_mixed_boolean():
    code = """
package main

func f(a, b, c bool) {
    if a && b || c {   // +1 (if) + 2 (&&->|| switch)
    }
}
"""
    assert _score(code) == 3


def test_complex_boolean():
    code = """
package main

func f(a, b, c, d bool) {
    if a || b || (c && d) {   // +1 (if) + 2 (|| sequence, then && subtree)
    }
}
"""
    assert _score(code) == 3


# --- Recursion ---


def test_recursion():
    code = """
package main

func factorial(n int) int {
    if n <= 1 {                      // +1
        return 1
    }
    return n * factorial(n - 1)      // +1 (recursion)
}
"""
    assert _score(code) == 2


def test_no_false_recursion():
    code = """
package main

func f(x int) int {
    return g(x)
}
"""
    assert _score(code) == 0


# --- Nested named func_literal (separate scoring) ---


def test_nested_function_separate_scoring():
    code = """
package main

func outer(x bool) {
    if x {}           // +1 for outer
    inner := func(y bool) {
        if y {}       // +1 for inner
    }
    _ = inner
}
"""
    scores = _scores(code)
    assert scores["outer"] == 1
    assert scores["inner"] == 1


# --- Closures ---


def test_inline_func_literal_increases_nesting():
    code = """
package main

func f(x int) {
    apply(func(v int) int {
        if v > 0 {          // +1 + 1 (nesting 1, inside inline closure)
            return v
        }
        return 0
    })
}
"""
    assert _score(code) == 2


def test_named_func_literal_scored_separately():
    code = """
package main

func outer() {
    if true {}   // +1 for outer
    handler := func(x bool) {
        if x {           // +1 for handler
            if true {}   // +1 + 1 for handler
        }
    }
    _ = handler
}
"""
    scores = _scores(code)
    assert scores["outer"] == 1
    assert scores["handler"] == 3


# --- Method ---


def test_method_declaration():
    code = """
package main

type Foo struct{}

func (f Foo) Bar(x bool) {
    if x {}   // +1
}
"""
    scores = _scores(code)
    assert scores["Bar"] == 1


# --- Go-specific ---


def test_go_statement_no_increment():
    code = """
package main

func f(x bool) {
    go func() {
        if x {}   // +1 + 1 (nesting 1 from inline closure)
    }()
}
"""
    assert _score(code) == 2


def test_defer_no_increment():
    code = """
package main

func f(x bool) {
    defer func() {
        if x {}   // +1 + 1 (nesting 1 from inline closure)
    }()
}
"""
    assert _score(code) == 2


def test_error_handling_pattern():
    code = """
package main

import "errors"

func f() error {
    err := doSomething()
    if err != nil {       // +1
        return err
    }
    return nil
}
"""
    assert _score(code) == 1


def test_if_with_initializer():
    code = """
package main

func f() {
    if err := doSomething(); err != nil {   // +1
    }
}
"""
    assert _score(code) == 1


# --- Comprehensive example ---


def test_comprehensive():
    code = """
package main

func process(items []int, flag bool) {
    if flag {                              // +1 (nesting 0)
        for i := 0; i < len(items); i++ { // +1 +1 (nesting 1)
            if items[i] > 0 {             // +1 +2 (nesting 2)
            } else if items[i] < 0 {      // +1 (no nesting bonus)
            } else {                       // +1 (no nesting bonus)
                switch items[i] {          // +1 +3 (nesting 3)
                case 0:
                }
            }
        }
    } else {                              // +1 (no nesting bonus)
    }
}
"""
    assert _score(code) == 13


# --- Edge cases ---


def test_not_operator_no_increment():
    code = """
package main

func f(x bool) {
    if !x {   // +1 (if only)
    }
}
"""
    assert _score(code) == 1


def test_multiple_return_values():
    code = """
package main

func f() (int, error) {
    if true {   // +1
        return 0, nil
    }
    return 1, nil
}
"""
    assert _score(code) == 1
