"""Tests for Python pattern detection."""

from vibelexity.patterns.shared import (
    check_module_level_execution,
    check_nested_imports,
    check_patterns,
    check_wildcard_imports,
)
from vibelexity.parsers.python import parse


def test_nested_imports():
    """Test that nested imports are detected."""
    code = """
import os

def foo():
    import sys
    return sys.path

class Bar:
    import pathlib
"""

    root = parse(code)
    violations = check_nested_imports(root)

    assert len(violations) == 2
    assert violations[0].type == "nested_import"
    assert violations[0].line == 5
    assert violations[1].type == "nested_import"
    assert violations[1].line == 9


def test_no_nested_imports():
    """Test that module-level imports are not flagged."""
    code = """
import os
import sys
from pathlib import Path
"""

    root = parse(code)
    violations = check_nested_imports(root)

    assert len(violations) == 0


def test_wildcard_imports():
    """Test that wildcard imports are detected."""
    code = """
from os import *
from pathlib import *
"""

    root = parse(code)
    violations = check_wildcard_imports(root)

    assert len(violations) == 2
    assert violations[0].type == "wildcard_import"
    assert violations[0].line == 2


def test_no_wildcard_imports():
    """Test that explicit imports are not flagged."""
    code = """
from os import path
from pathlib import Path, PosixPath
"""
    root = parse(code)
    violations = check_wildcard_imports(root)

    assert len(violations) == 0


def test_module_level_function_call():
    """Flag module-level function calls."""
    code = """
from mymodule import foo

result = foo()
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 1
    assert violations[0].type == "module_level_call"


def test_module_level_assignment():
    """Flag module-level assignments."""
    code = """
from mymodule import Bar

item = Bar()
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 1


def test_inside_main_guard():
    """Don't flag code inside if __name__ == '__main__'."""
    code = """
def main():
    foo()

if __name__ == "__main__":
    main()
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_inside_type_checking():
    """Don't flag code inside if TYPE_CHECKING block."""
    code = """
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections import deque
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_function_definitions_allowed():
    """Don't flag function/class definitions."""
    code = """
def foo():
    pass

class Bar:
    pass
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_imports_allowed():
    """Don't flag imports."""
    code = """
from os import path
import json
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_module_level_for_loop():
    """Flag module-level for loops."""
    code = """
from mymodule import process

for item in items:
    process(item)
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert any(
        v.description.startswith("Module-level code execution") for v in violations
    )


def test_nested_main_guard():
    """Don't flag code deeply nested in main guard."""
    code = """
if __name__ == "__main__":
    data = load_data()
    print(data)
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_constant_assignment():
    """Don't flag constant assignments (no execution)."""
    code = """
MAX_SIZE = 100
PATH = "/usr/local/bin"
CONFIG = {"key": "value"}
ITEMS = [1, 2, 3]
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_assignment_with_execution():
    """Flag assignments that involve execution."""
    code = """
result = calculate()
path = os.getcwd()
items = [x for x in range(10)]
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 3


def test_nested_import_in_type_checking_block():
    """Nested imports inside TYPE_CHECKING blocks are allowed."""
    code = """
from typing import TYPE_CHECKING

def foo():
    if TYPE_CHECKING:
        import collections
"""
    root = parse(code)
    violations = check_nested_imports(root)
    assert len(violations) == 0


def test_nested_import_in_class_method_type_checking():
    """Nested imports in methods inside TYPE_CHECKING context."""
    code = """
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections import deque

class MyClass:
    def method(self):
        import itertools
"""
    root = parse(code)
    violations = check_nested_imports(root)
    assert len(violations) == 1


def test_module_level_while_loop():
    """Flag module-level while loops."""
    code = """
from mymodule import process

while True:
    process(item)
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert any(
        v.description.startswith("Module-level code execution") for v in violations
    )


def test_module_level_try_statement():
    """Flag module-level try/except blocks."""
    code = """
try:
    import json
except ImportError:
    pass
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert any(
        v.description.startswith("Module-level code execution") for v in violations
    )


def test_module_level_with_statement():
    """Flag module-level with statements."""
    code = """
with open("file.txt") as f:
    data = f.read()
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert any(
        v.description.startswith("Module-level code execution") for v in violations
    )


def test_module_level_if_statements_allowed():
    """Module-level if statements that are main guards or type checking are allowed."""
    code = """
if __name__ == "__main__":
    main()

if TYPE_CHECKING:
    from typing import Dict
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_involves_execution_with_builtins():
    """Operations involving builtins count as execution."""
    code = """
result = len([1, 2, 3])
path = str(os.getcwd())
value = bool(some_var)
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 3


def test_module_level_call_in_nested_if():
    """Call inside nested module-level if statement."""
    code = """
if some_condition:
    if True:
        result = compute()
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) >= 1


def test_nested_type_checking_import_detection():
    """Deeply nested TYPE_CHECKING import detection."""
    code = """
from typing import TYPE_CHECKING

def outer():
    def inner():
        if TYPE_CHECKING:
            import sys
"""
    root = parse(code)
    violations = check_nested_imports(root)
    assert len(violations) == 0


def test_constant_assignment_with_dict_literal():
    """Dict literal assignments don't count as execution."""
    code = """
CONFIG = {"key": "value", "setting": True}
OPTIONS = {"a": 1, "b": 2}
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_constant_assignment_with_set_literal():
    """Set literal assignments don't count as execution."""
    code = """
    ITEMS = {1, 2, 3}
    NAMES = {"a", "b", "c"}
    """
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_comment_only_lines():
    """Comment-only lines should not be flagged."""
    code = """
# This is a comment
# Another comment
# Third comment
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_standalone_identifier():
    """Standalone identifier should not be flagged."""
    code = """
x
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_standalone_string():
    """Standalone string should not be flagged."""
    code = """
'hello'
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_standalone_number():
    """Standalone number should not be flagged."""
    code = """
42
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_standalone_float():
    """Standalone float should not be flagged."""
    code = """
3.14
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_standalone_true_false_none():
    """Standalone True/False/None should not be flagged."""
    code = """
True
False
None
"""
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 0


def test_binary_operator_expression():
    """Binary operator expressions should be flagged (they involve execution)."""
    code = """
    x + y
    a * b + c
    """
    root = parse(code)
    violations = check_module_level_execution(root)
    assert len(violations) == 2


def test_ignore_comment_kebab_case():
    """Kebab-case pattern names should work."""
    code = """# vibelexity-ignore:nested-import
 
def foo():
    import sys
    return sys.path
  """
    root = parse(code)
    violations = [
        v
        for v in check_patterns("python", root, source=code)
        if v.type == "nested_import"
    ]
    assert len(violations) == 0


def test_ignore_comment_snake_case():
    """Snake_case pattern names should work."""
    code = """# vibelexity-ignore:nested_import
 
def foo():
    import sys
    return sys.path
  """
    root = parse(code)
    violations = [
        v
        for v in check_patterns("python", root, source=code)
        if v.type == "nested_import"
    ]
    assert len(violations) == 0
