import pytest

from test.stack.test_helpers import (
    count_defs_refs,
    get_definition_precedences,
    resolve_symbol,
    setup_stack_resolution,
)


@pytest.mark.blends_test_group("stack_unittesting")
def test_linear_reassignment_returns_all_same_scope_defs() -> None:
    view, record = setup_stack_resolution("case_linear_reassignment.py")
    defs, refs = count_defs_refs(view, "x")

    assert defs == 2, f"Expected 2 defs of 'x', got {defs}"  # noqa: PLR2004
    assert refs == 1, f"Expected 1 ref to 'x', got {refs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 2, (  # noqa: PLR2004
        f"Uniform precedence should return all same-scope defs, got {len(result)}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_parameter_then_reassignment_returns_all_defs() -> None:
    view, record = setup_stack_resolution("case_param_reassignment.py")
    defs, refs = count_defs_refs(view, "x")

    assert defs >= 2, f"Expected at least 2 defs of 'x', got {defs}"  # noqa: PLR2004
    assert refs >= 1, f"Expected at least 1 ref to 'x', got {refs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) >= 2, (  # noqa: PLR2004
        f"Uniform precedence should return all same-scope defs, got {len(result)}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_different_symbols_not_affected() -> None:
    view, record = setup_stack_resolution("case_different_symbols.py")
    x_defs, x_refs = count_defs_refs(view, "x")
    y_defs, _y_refs = count_defs_refs(view, "y")

    assert x_defs == 1, f"Expected 1 def of 'x', got {x_defs}"
    assert x_refs == 1, f"Expected 1 ref to 'x', got {x_refs}"
    assert y_defs == 1, f"Expected 1 def of 'y', got {y_defs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 1, f"'x' should resolve to 1 def, got {len(result)}"


@pytest.mark.blends_test_group("stack_unittesting")
def test_multiple_reassignments_returns_all_defs() -> None:
    view, record = setup_stack_resolution("case_multiple_reassignment.py")
    defs, refs = count_defs_refs(view, "x")

    assert defs == 3, f"Expected 3 defs of 'x', got {defs}"  # noqa: PLR2004
    assert refs == 1, f"Expected 1 ref to 'x', got {refs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 3, (  # noqa: PLR2004
        f"Uniform precedence should return all same-scope defs, got {len(result)}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_uniform_precedence_for_parameter_and_assignments() -> None:
    view, _record = setup_stack_resolution("case_source_order_param.py")
    precedences = get_definition_precedences(view, "x")

    assert len(precedences) >= 2, (  # noqa: PLR2004
        f"Expected at least 2 defs of 'x', got {len(precedences)}"
    )
    assert all(p == 1 for p in precedences), (
        f"All same-scope binding precedences should be 1: {precedences}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_uniform_precedence_for_multiple_reassignments() -> None:
    view, _record = setup_stack_resolution("case_source_order_multi.py")
    precedences = get_definition_precedences(view, "x")

    assert len(precedences) >= 2, (  # noqa: PLR2004
        f"Expected at least 2 defs of 'x', got {len(precedences)}"
    )
    assert all(p == 1 for p in precedences), (
        f"All same-scope binding precedences should be 1: {precedences}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_uniform_precedence_for_different_symbols() -> None:
    view, _record = setup_stack_resolution("case_source_order_multi_symbols.py")

    x_precs = get_definition_precedences(view, "x")
    y_precs = get_definition_precedences(view, "y")

    assert len(x_precs) >= 2, f"Expected at least 2 defs of 'x', got {len(x_precs)}"  # noqa: PLR2004
    assert len(y_precs) >= 2, f"Expected at least 2 defs of 'y', got {len(y_precs)}"  # noqa: PLR2004
    assert all(p == 1 for p in x_precs), f"'x' precedences should all be 1: {x_precs}"
    assert all(p == 1 for p in y_precs), f"'y' precedences should all be 1: {y_precs}"


@pytest.mark.blends_test_group("stack_unittesting")
def test_nested_function_definition_returns_all_defs() -> None:
    view, record = setup_stack_resolution("case_source_order_nested_func.py")
    x_precs = get_definition_precedences(view, "x")

    assert len(x_precs) >= 2, f"Expected at least 2 defs of 'x', got {len(x_precs)}"  # noqa: PLR2004

    result = resolve_symbol(view, record, "x")
    assert len(result) >= 2, (  # noqa: PLR2004
        f"Uniform precedence should return all same-scope defs, got {len(result)}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_single_definition_no_reorder() -> None:
    view, record = setup_stack_resolution("case_different_symbols.py")

    x_precs = get_definition_precedences(view, "x")
    y_precs = get_definition_precedences(view, "y")

    assert len(x_precs) == 1, f"Expected 1 def of 'x', got {len(x_precs)}"
    assert len(y_precs) == 1, f"Expected 1 def of 'y', got {len(y_precs)}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 1, f"'x' should resolve to 1 def, got {len(result)}"


@pytest.mark.blends_test_group("stack_unittesting")
def test_conditional_branches_return_both_defs() -> None:
    view, record = setup_stack_resolution("case_conditional_branches.py")
    defs, refs = count_defs_refs(view, "x")

    assert defs == 2, f"Expected 2 defs of 'x', got {defs}"  # noqa: PLR2004
    assert refs == 1, f"Expected 1 ref to 'x', got {refs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 2, (  # noqa: PLR2004
        f"Both branch defs should be returned, got {len(result)}"
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_parameter_shadows_module_level_variable() -> None:
    view, record = setup_stack_resolution("case_param_shadows_module.py")
    defs, refs = count_defs_refs(view, "x")

    assert defs >= 2, f"Expected at least 2 defs of 'x', got {defs}"  # noqa: PLR2004
    assert refs >= 1, f"Expected at least 1 ref to 'x', got {refs}"

    result = resolve_symbol(view, record, "x")
    assert len(result) == 1, f"Parameter should shadow module-level x, got {len(result)}"
