import ast

import libcst as cst

from core.languages.python.cst_utils import ImportTrackingVisitor, evaluate_expression, find_init, get_base_class_name


def test_import_tracking_star_import() -> None:
    code = "from module import *"
    tree = ast.parse(code)
    visitor = ImportTrackingVisitor()
    visitor.visit(tree)
    assert visitor.imported_names == set()


def test_import_tracking_regular_import() -> None:
    code = "from module import foo, bar"
    tree = ast.parse(code)
    visitor = ImportTrackingVisitor()
    visitor.visit(tree)
    assert visitor.imported_names == {"foo", "bar"}


def test_get_base_class_name_simple() -> None:
    module = cst.parse_module("class Foo(Bar): pass")
    class_def = module.body[0]
    assert isinstance(class_def, cst.ClassDef)
    base = class_def.bases[0]
    assert get_base_class_name(base) == "Bar"


def test_get_base_class_name_attribute() -> None:
    module = cst.parse_module("class Foo(module.Bar): pass")
    class_def = module.body[0]
    assert isinstance(class_def, cst.ClassDef)
    base = class_def.bases[0]
    assert get_base_class_name(base) == "Bar"


def test_get_base_class_name_unknown_expression() -> None:
    module = cst.parse_module("class Foo(get_base()): pass")
    class_def = module.body[0]
    assert isinstance(class_def, cst.ClassDef)
    base = class_def.bases[0]
    assert get_base_class_name(base) is None


def test_evaluate_expression_integer() -> None:
    node = cst.Integer("42")
    assert evaluate_expression(node) == 42


def test_evaluate_expression_hexadecimal() -> None:
    """Test that hexadecimal integer literals (0x prefix) are correctly evaluated."""
    assert evaluate_expression(cst.Integer("0x00")) == 0
    assert evaluate_expression(cst.Integer("0xFF")) == 255
    assert evaluate_expression(cst.Integer("0xDEADBEEF")) == 0xDEADBEEF


def test_evaluate_expression_octal() -> None:
    """Test that octal integer literals (0o prefix) are correctly evaluated."""
    assert evaluate_expression(cst.Integer("0o0")) == 0
    assert evaluate_expression(cst.Integer("0o77")) == 63
    assert evaluate_expression(cst.Integer("0o755")) == 493


def test_evaluate_expression_binary() -> None:
    """Test that binary integer literals (0b prefix) are correctly evaluated."""
    assert evaluate_expression(cst.Integer("0b0")) == 0
    assert evaluate_expression(cst.Integer("0b1010")) == 10
    assert evaluate_expression(cst.Integer("0b11111111")) == 255


def test_evaluate_expression_float() -> None:
    node = cst.Float("3.14")
    assert evaluate_expression(node) == 3


def test_evaluate_expression_binary_add() -> None:
    node = cst.BinaryOperation(left=cst.Integer("2"), operator=cst.Add(), right=cst.Integer("3"))
    assert evaluate_expression(node) == 5


def test_evaluate_expression_unary_minus() -> None:
    node = cst.UnaryOperation(operator=cst.Minus(), expression=cst.Integer("5"))
    assert evaluate_expression(node) == -5


def test_evaluate_expression_unevaluable() -> None:
    node = cst.Name("x")
    assert evaluate_expression(node) is None


def test_evaluate_expression_unsupported_binary_op() -> None:
    node = cst.BinaryOperation(left=cst.Integer("10"), operator=cst.Divide(), right=cst.Integer("2"))
    assert evaluate_expression(node) is None


def test_find_init_present() -> None:
    code = """
class Foo:
    def __init__(self):
        pass
"""
    tree = ast.parse(code)
    assert find_init(tree) is True


def test_find_init_absent() -> None:
    code = """
class Foo:
    def bar(self):
        pass
"""
    tree = ast.parse(code)
    assert find_init(tree) is False


def test_find_init_nested() -> None:
    code = """
class Outer:
    class Inner:
        def __init__(self):
            pass
"""
    tree = ast.parse(code)
    assert find_init(tree) is True


def test_find_init_empty_module() -> None:
    tree = ast.parse("")
    assert find_init(tree) is False
