"""Tests for repeated calls pattern detection."""

from vibelexity.parsers.python import parse
from vibelexity.patterns.loops import check_repeated_calls
from vibelexity.patterns.shared import check_patterns


def test_basic_two_consecutive_calls():
    """Two consecutive calls should NOT be flagged (threshold is 3)."""
    code = """
parser.add_argument("--foo", help="Foo option")
parser.add_argument("--bar", help="Bar option")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_three_consecutive_calls():
    """Detect three consecutive calls to the same method."""
    code = """
parser.add_argument("--show-dupes", dest="show_dupes", action="store_true")
parser.add_argument("--perf", dest="show_perf", action="store_true")
parser.add_argument("--verbose", dest="verbose", action="store_true")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert "3 consecutive calls" in violations[0].description


def test_multiline_calls():
    """Detect consecutive calls that span multiple lines each."""
    code = """
parser.add_argument(
    "--show-dupes",
    dest="show_dupes",
    action="store_true",
    help="Show detailed code duplication sites",
)
parser.add_argument(
    "--perf",
    dest="show_perf",
    action="store_true",
    help="Show timing information for each metric",
)
parser.add_argument(
    "--verbose",
    dest="verbose",
    action="store_true",
    help="Show verbose output",
)
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert violations[0].type == "repeated_calls"
    assert "3 consecutive calls" in violations[0].description


def test_different_objects_no_violation():
    """Different objects should not be flagged."""
    code = """
parser1.add_argument("--foo")
parser2.add_argument("--bar")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_different_methods_no_violation():
    """Different methods on the same object should not be flagged."""
    code = """
parser.add_argument("--foo")
parser.set_defaults(foo=True)
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_single_call_no_violation():
    """A single call should not be flagged."""
    code = """
parser.add_argument("--foo")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_non_consecutive_calls_no_violation():
    """Non-consecutive calls with different code between them should not be flagged."""
    code = """
parser.add_argument("--foo")
x = compute_something()
parser.add_argument("--bar")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_multiple_groups():
    """Detect multiple separate groups of repeated calls."""
    code = """
parser.add_argument("--foo")
parser.add_argument("--bar")
parser.add_argument("--baz")

config.set_option("a", 1)
config.set_option("b", 2)
config.set_option("c", 3)
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 2
    descriptions = [v.description for v in violations]
    assert any("parser.add_argument" in d for d in descriptions)
    assert any("config.set_option" in d for d in descriptions)


def test_calls_with_comments_between():
    """Calls with comments between them are NOT detected (comments are named nodes)."""
    code = """
parser.add_argument("--foo")
# This is a comment
parser.add_argument("--bar")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    # Comments ARE named nodes in tree-sitter Python, so the `.` (immediate sibling)
    # operator in the S-query doesn't match when there's a comment between calls.
    # This is expected behavior - comments break the adjacency.
    assert len(violations) == 0


def test_empty_code():
    """Empty code should produce no violations."""
    code = ""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_no_method_calls():
    """Code without method calls should produce no violations."""
    code = """
x = 1
y = 2
z = x + y
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_function_calls_not_method_calls():
    """Regular function calls (not method calls) should not be flagged."""
    code = """
print("hello")
print("world")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    # print() is a function call, not a method call (no object.method pattern)
    assert len(violations) == 0


def test_chained_method_calls():
    """Chained method calls are NOT detected (object is a call, not identifier)."""
    code = """
builder.add("a").add("b")
builder.add("c").add("d")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    # For chained calls like `builder.add("a").add("b")`, the outermost call's
    # function attribute has object=call (not identifier), so the S-query
    # pattern `object: (identifier)` doesn't match. This is a known limitation.
    assert len(violations) == 0


def test_registered_in_shared_patterns():
    """Verify check_repeated_calls is registered in shared patterns."""
    code = """
 parser.add_argument("--foo")
 parser.add_argument("--bar")
 parser.add_argument("--baz")
 """
    root = parse(code)
    violations = check_patterns("python", root, patterns=["check_repeated_calls"])
    assert len(violations) == 1
    assert violations[0].type == "repeated_calls"


def test_ignore_comment():
    """Verify ignore comment suppresses repeated_calls violations."""
    code = """# vibelexity-ignore:repeated-calls

parser.add_argument("--foo")
parser.add_argument("--bar")
"""
    root = parse(code)
    violations = check_patterns("python", root, source=code)
    # Filter to only repeated_calls violations
    repeated_calls_violations = [v for v in violations if v.type == "repeated_calls"]
    assert len(repeated_calls_violations) == 0


def test_ignore_comment_snake_case():
    """Verify snake_case ignore pattern works."""
    code = """# vibelexity-ignore:repeated_calls

parser.add_argument("--foo")
parser.add_argument("--bar")
"""
    root = parse(code)
    violations = check_patterns("python", root, source=code)
    repeated_calls_violations = [v for v in violations if v.type == "repeated_calls"]
    assert len(repeated_calls_violations) == 0


def test_violation_line_number():
    """Verify violation reports the first call's line number."""
    code = """
parser.add_argument("--foo")
parser.add_argument("--bar")
parser.add_argument("--baz")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert violations[0].line == 2  # First call is on line 2


def test_severity_is_info():
    """Verify violations have 'info' severity."""
    code = """
parser.add_argument("--verbose")
parser.add_argument("--quiet")
parser.add_argument("--debug")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert violations[0].severity == "info"


def test_inside_function():
    """Detect repeated calls inside a function body."""
    code = """
def setup_parser(parser):
    parser.add_argument("--verbose")
    parser.add_argument("--quiet")
    parser.add_argument("--debug")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert "3 consecutive calls" in violations[0].description


def test_inside_class_method():
    """Detect repeated calls inside a class method."""
    code = """
class CLI:
    def configure(self):
        self.parser.add_argument("--verbose")
        self.parser.add_argument("--quiet")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    # self.parser is an attribute access, not a simple identifier
    # This case won't match our current query pattern
    assert len(violations) == 0


def test_mixed_consecutive_and_non_consecutive():
    """Test a mix of consecutive and non-consecutive calls."""
    code = """
parser.add_argument("--a")
parser.add_argument("--b")

# Lots of code here to break the sequence
def helper():
    pass

x = 1
y = 2
z = 3
a = 4
b = 5
c = 6
d = 7
e = 8
f = 9
g = 10
h = 11
i = 12

parser.add_argument("--c")
parser.add_argument("--d")
parser.add_argument("--e")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    # Only the last three are consecutive
    assert len(violations) == 1
    assert "3 consecutive calls" in violations[0].description


def test_basic_three_consecutive_function_calls():
    """Detect three consecutive calls to the same function."""
    code = """
print("hello")
print("world")
print("vibelexity")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 1
    assert violations[0].type == "repeated_calls"
    assert "3 consecutive calls" in violations[0].description
    assert "print" in violations[0].description


def test_two_consecutive_function_calls():
    """Two consecutive function calls should NOT be flagged (threshold is 3)."""
    code = """
print("hello")
print("world")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_different_functions_no_violation():
    """Different functions should not be flagged."""
    code = """
print("hello")
log.info("world")
sys.exit(1)
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 0


def test_mixed_method_and_function_calls():
    """Mixed method and function calls should be grouped separately."""
    code = """
parser.add_argument("--verbose")
parser.add_argument("--quiet")
parser.add_argument("--debug")

print("starting")
print("loading")
print("done")
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 2
    descriptions = [v.description for v in violations]
    assert any("parser.add_argument" in d for d in descriptions)
    assert any("print" in d for d in descriptions)


def test_function_call_vs_method_call_no_crossover():
    """Same name but one is function, one is method should be separate targets."""
    code = """
config.get("a")
config.get("b")
config.get("c")

get_config()
get_config()
get_config()
"""
    root = parse(code)
    violations = check_repeated_calls(root)
    assert len(violations) == 2
    descriptions = [v.description for v in violations]
    assert any("config.get" in d for d in descriptions)
    assert any("get_config" in d for d in descriptions)
