import os
import tempfile
from pathlib import Path

from codeflash_python._model import FunctionParent
from codeflash_python.analysis._discovery import FunctionToOptimize
from codeflash_python.test_discovery import TestsInFile, TestType
from codeflash_python.test_discovery.discovery import discover_unit_tests
from codeflash_python.test_discovery.filtering import (
    analyze_imports_in_test_file,
    filter_test_files_by_imports,
)
from codeflash_python.testing.models import TestConfig


def test_unit_test_discovery_pytest():
    project_path = Path(__file__).parent.resolve() / "code_to_optimize"
    tests_path = project_path / "tests" / "pytest"
    test_config = TestConfig(
        tests_root=tests_path,
        project_root_path=project_path,
        test_framework="pytest",
        tests_project_rootdir=tests_path.parent,
    )
    tests, _, _ = discover_unit_tests(test_config)
    assert len(tests) > 0


def test_benchmark_test_discovery_pytest():
    project_path = Path(__file__).parent.resolve() / "code_to_optimize"
    tests_path = project_path / "tests" / "pytest" / "benchmarks"
    test_config = TestConfig(
        tests_root=tests_path,
        project_root_path=project_path,
        test_framework="pytest",
        tests_project_rootdir=tests_path.parent,
    )
    tests, _, _ = discover_unit_tests(test_config)
    assert len(tests) == 1  # Should not discover benchmark tests


def test_unit_test_discovery_unittest():
    project_path = Path(__file__).parent.resolve() / "code_to_optimize"
    test_path = project_path / "tests" / "unittest"
    test_config = TestConfig(
        tests_root=project_path,
        project_root_path=project_path,
        test_framework="unittest",
        tests_project_rootdir=project_path.parent,
    )
    os.chdir(project_path)
    tests, _, _ = discover_unit_tests(test_config)
    # assert len(tests) > 0
    # Unittest discovery within a pytest environment does not work


def test_benchmark_unit_test_discovery_pytest():
    with tempfile.TemporaryDirectory() as tmpdirname:
        # Create a dummy test file
        test_file_path = Path(tmpdirname) / "test_dummy.py"
        test_file_content = """
from bubble_sort import sorter

def test_benchmark_sort(benchmark):
     benchmark(sorter, [5, 4, 3, 2, 1, 0])

def test_normal_test():
    assert sorter(list(reversed(range(100)))) == list(range(100))

def test_normal_test2():
    assert sorter(list(reversed(range(100)))) == list(range(100))"""
        test_file_path.write_text(test_file_content)
        path_obj_tempdirname = Path(tmpdirname)

        # Create a file that the test file is testing
        code_file_path = path_obj_tempdirname / "bubble_sort.py"
        code_file_content = """
def sorter(arr):
    return sorted(arr)"""
        code_file_path.write_text(code_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tempdirname,
            project_root_path=path_obj_tempdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tempdirname.parent,
        )

        # Discover tests
        tests, _, _ = discover_unit_tests(test_config)
        assert len(tests) == 1
        assert "bubble_sort.sorter" in tests
        assert len(tests["bubble_sort.sorter"]) == 2
        functions = [
            test.tests_in_file.test_function
            for test in tests["bubble_sort.sorter"]
        ]
        assert "test_normal_test" in functions
        assert "test_normal_test2" in functions
        assert "test_benchmark_sort" not in functions


def test_discover_tests_pytest_with_temp_dir_root():
    with tempfile.TemporaryDirectory() as tmpdirname:
        # Create a dummy test file
        test_file_path = Path(tmpdirname) / "test_dummy.py"
        test_file_content = (
            "import pytest\n"
            "from dummy_code import dummy_function\n\n"
            "def test_dummy_function():\n"
            "    assert dummy_function() is True\n"
            "@pytest.mark.parametrize('param', [True])\n"
            "def test_dummy_parametrized_function(param):\n"
            "    assert dummy_function() is True\n"
        )
        test_file_path.write_text(test_file_content)
        path_obj_tempdirname = Path(tmpdirname)

        # Create a file that the test file is testing
        code_file_path = path_obj_tempdirname / "dummy_code.py"
        code_file_content = "def dummy_function():\n    return True\n"
        code_file_path.write_text(code_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tempdirname,
            project_root_path=path_obj_tempdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tempdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the dummy test file is discovered
        assert len(discovered_tests) == 1
        assert len(discovered_tests["dummy_code.dummy_function"]) == 2
        dummy_tests = discovered_tests["dummy_code.dummy_function"]
        assert all(
            test.tests_in_file.test_file.resolve() == test_file_path.resolve()
            for test in dummy_tests
        )
        assert {test.tests_in_file.test_function for test in dummy_tests} == {
            "test_dummy_parametrized_function[True]",
            "test_dummy_function",
        }


def test_discover_tests_pytest_with_multi_level_dirs():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create multi-level directories
        level1_dir = path_obj_tmpdirname / "level1"
        level2_dir = level1_dir / "level2"
        level2_dir.mkdir(parents=True)

        # Create code files at each level
        root_code_file_path = path_obj_tmpdirname / "root_code.py"
        root_code_file_content = "def root_function():\n    return True\n"
        root_code_file_path.write_text(root_code_file_content)

        level1_code_file_path = level1_dir / "level1_code.py"
        level1_code_file_content = "def level1_function():\n    return True\n"
        level1_code_file_path.write_text(level1_code_file_content)

        level2_code_file_path = level2_dir / "level2_code.py"
        level2_code_file_content = "def level2_function():\n    return True\n"
        level2_code_file_path.write_text(level2_code_file_content)

        # Create a test file at the root level
        root_test_file_path = path_obj_tmpdirname / "test_root.py"
        root_test_file_content = (
            "from root_code import root_function\n\n"
            "def test_root_function():\n"
            "    assert True\n"
            "    assert root_function() is True\n"
        )
        root_test_file_path.write_text(root_test_file_content)

        # Create a test file at level 1
        level1_test_file_path = level1_dir / "test_level1.py"
        level1_test_file_content = (
            "from level1_code import level1_function\n\n"
            "def test_level1_function():\n"
            "    assert True\n"
            "    assert level1_function() is True\n"
        )
        level1_test_file_path.write_text(level1_test_file_content)

        # Create a test file at level 2
        level2_test_file_path = level2_dir / "test_level2.py"
        level2_test_file_content = (
            "from level2_code import level2_function\n\n"
            "def test_level2_function():\n"
            "    assert True\n"
            "    assert level2_function() is True\n"
        )
        level2_test_file_path.write_text(level2_test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test files at all levels are discovered
        assert len(discovered_tests) == 3
        discovered_root_test = next(
            iter(discovered_tests["root_code.root_function"])
        ).tests_in_file.test_file
        assert discovered_root_test.resolve() == root_test_file_path.resolve()
        discovered_level1_test = next(
            iter(discovered_tests["level1.level1_code.level1_function"])
        ).tests_in_file.test_file
        assert (
            discovered_level1_test.resolve() == level1_test_file_path.resolve()
        )

        discovered_level2_test = next(
            iter(discovered_tests["level1.level2.level2_code.level2_function"])
        ).tests_in_file.test_file
        assert (
            discovered_level2_test.resolve() == level2_test_file_path.resolve()
        )


def test_discover_tests_pytest_dirs():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create multi-level directories
        level1_dir = Path(tmpdirname) / "level1"
        level2_dir = level1_dir / "level2"
        level2_dir.mkdir(parents=True)
        level3_dir = level1_dir / "level3"
        level3_dir.mkdir(parents=True)

        # Create code files at each level
        root_code_file_path = path_obj_tmpdirname / "root_code.py"
        root_code_file_content = "def root_function():\n    return True\n"
        root_code_file_path.write_text(root_code_file_content)

        level1_code_file_path = level1_dir / "level1_code.py"
        level1_code_file_content = "def level1_function():\n    return True\n"
        level1_code_file_path.write_text(level1_code_file_content)

        level2_code_file_path = level2_dir / "level2_code.py"
        level2_code_file_content = "def level2_function():\n    return True\n"
        level2_code_file_path.write_text(level2_code_file_content)

        level3_code_file_path = level3_dir / "level3_code.py"
        level3_code_file_content = "def level3_function():\n    return True\n"
        level3_code_file_path.write_text(level3_code_file_content)

        # Create a test file at the root level
        root_test_file_path = path_obj_tmpdirname / "test_root.py"
        root_test_file_content = (
            "from root_code import root_function\n\n"
            "def test_root_function():\n"
            "    assert True\n"
            "    assert root_function() is True\n"
        )
        root_test_file_path.write_text(root_test_file_content)

        # Create a test file at level 1
        level1_test_file_path = level1_dir / "test_level1.py"
        level1_test_file_content = (
            "from level1_code import level1_function\n\n"
            "def test_level1_function():\n"
            "    assert True\n"
            "    assert level1_function() is True\n"
        )
        level1_test_file_path.write_text(level1_test_file_content)

        # Create a test file at level 2
        level2_test_file_path = level2_dir / "test_level2.py"
        level2_test_file_content = (
            "from level2_code import level2_function\n\n"
            "def test_level2_function():\n"
            "    assert True\n"
            "    assert level2_function() is True\n"
        )
        level2_test_file_path.write_text(level2_test_file_content)

        level3_test_file_path = level3_dir / "test_level3.py"
        level3_test_file_content = (
            "from level3_code import level3_function\n\n"
            "def test_level3_function():\n"
            "    assert True\n"
            "    assert level3_function() is True\n"
        )
        level3_test_file_path.write_text(level3_test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test files at all levels are discovered
        assert len(discovered_tests) == 4
        discovered_root_test = next(
            iter(discovered_tests["root_code.root_function"])
        ).tests_in_file.test_file
        assert discovered_root_test.resolve() == root_test_file_path.resolve()
        discovered_level1_test = next(
            iter(discovered_tests["level1.level1_code.level1_function"])
        ).tests_in_file.test_file
        assert (
            discovered_level1_test.resolve() == level1_test_file_path.resolve()
        )
        discovered_level2_test = next(
            iter(discovered_tests["level1.level2.level2_code.level2_function"])
        ).tests_in_file.test_file
        assert (
            discovered_level2_test.resolve() == level2_test_file_path.resolve()
        )

        discovered_level3_test = next(
            iter(discovered_tests["level1.level3.level3_code.level3_function"])
        ).tests_in_file.test_file
        assert (
            discovered_level3_test.resolve() == level3_test_file_path.resolve()
        )


def test_discover_tests_pytest_with_class():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create a code file with a class
        code_file_path = path_obj_tmpdirname / "some_class_code.py"
        code_file_content = "class SomeClass:\n    def some_method(self):\n        return True\n"
        code_file_path.write_text(code_file_content)

        # Create a test file with a test class and a test method
        test_file_path = path_obj_tmpdirname / "test_some_class.py"
        test_file_content = (
            "from some_class_code import SomeClass\n\n"
            "def test_some_method():\n"
            "    instance = SomeClass()\n"
            "    assert instance.some_method() is True\n"
        )
        test_file_path.write_text(test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test class and method are discovered
        assert len(discovered_tests) == 1
        discovered_class_test = next(
            iter(discovered_tests["some_class_code.SomeClass.some_method"])
        ).tests_in_file.test_file
        assert discovered_class_test.resolve() == test_file_path.resolve()


def test_discover_tests_pytest_with_double_nested_directories():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create nested directories
        nested_dir = path_obj_tmpdirname / "nested" / "more_nested"
        nested_dir.mkdir(parents=True)

        # Create a code file with a class in the nested directory
        code_file_path = nested_dir / "nested_class_code.py"
        code_file_content = "class NestedClass:\n    def nested_method(self):\n        return True\n"
        code_file_path.write_text(code_file_content)

        # Create a test file with a test class and a test method in the nested directory
        test_file_path = nested_dir / "test_nested_class.py"
        test_file_content = (
            "from nested_class_code import NestedClass\n\n"
            "def test_nested_method():\n"
            "    instance = NestedClass()\n"
            "    assert instance.nested_method() is True\n"
        )
        test_file_path.write_text(test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test class and method are discovered
        assert len(discovered_tests) == 1
        discovered_nested_test = next(
            iter(
                discovered_tests[
                    "nested.more_nested.nested_class_code.NestedClass.nested_method"
                ]
            )
        ).tests_in_file.test_file
        assert discovered_nested_test.resolve() == test_file_path.resolve()


def test_discover_tests_with_code_in_dir_and_test_in_subdir():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create a directory for the code file
        code_dir = path_obj_tmpdirname / "code"
        code_dir.mkdir()

        # Create a code file in the code directory
        code_file_path = code_dir / "some_code.py"
        code_file_content = "def some_function():\n    return True\n"
        code_file_path.write_text(code_file_content)

        # Create a subdirectory for the test file within the code directory
        test_subdir = code_dir / "tests"
        test_subdir.mkdir()

        # Create a test file in the test subdirectory
        test_file_path = test_subdir / "test_some_code.py"
        test_file_content = (
            "import sys\n"
            "import os\n"
            # I am suspicious of this line, we should not need to insert the code directory into the path
            "sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\n"
            "from some_code import some_function\n\n"
            "def test_some_function():\n"
            "    assert some_function() is True\n"
        )
        test_file_path.write_text(test_file_content)

        # Create a TestConfig with the code directory as the root
        test_config = TestConfig(
            tests_root=test_subdir,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=test_subdir.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test file is discovered and associated with the code file
        assert len(discovered_tests) == 1
        discovered_test_file = next(
            iter(discovered_tests["code.some_code.some_function"])
        ).tests_in_file.test_file
        assert discovered_test_file.resolve() == test_file_path.resolve()


def test_discover_tests_pytest_with_nested_class():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)
        # Create a code file with a nested class
        code_file_path = path_obj_tmpdirname / "nested_class_code.py"
        code_file_content = "class OuterClass:\n    class InnerClass:\n        def inner_method(self):\n            return True\n"
        code_file_path.write_text(code_file_content)

        # Create a test file with a test for the nested class method
        test_file_path = path_obj_tmpdirname / "test_nested_class.py"
        test_file_content = (
            "from nested_class_code import OuterClass\n\n"
            "def test_inner_method():\n"
            "    instance = OuterClass.InnerClass()\n"
            "    assert instance.inner_method() is True\n"
        )
        test_file_path.write_text(test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test for the nested class method is discovered
        assert len(discovered_tests) == 1
        discovered_inner_test = next(
            iter(
                discovered_tests[
                    "nested_class_code.OuterClass.InnerClass.inner_method"
                ]
            )
        ).tests_in_file.test_file
        assert discovered_inner_test.resolve() == test_file_path.resolve()


def test_discover_tests_pytest_separate_moduledir():
    with tempfile.TemporaryDirectory() as tmpdirname:
        rootdir = Path(tmpdirname)
        # Create a code file with a nested class
        codedir = rootdir / "src" / "mypackage"
        codedir.mkdir(parents=True)
        code_file_path = codedir / "code.py"
        code_file_content = "def find_common_tags(articles):\n    if not articles:\n        return set()\n"
        code_file_path.write_text(code_file_content)

        # Create a test file with a test for the nested class method
        testdir = rootdir / "tests"
        testdir.mkdir()
        test_file_path = testdir / "test_code.py"
        test_file_content = (
            "from mypackage.code import find_common_tags\n\n"
            "def test_common_tags():\n"
            "    assert find_common_tags(None) == set()\n"
        )
        test_file_path.write_text(test_file_content)

        # Create a TestConfig with the temporary directory as the root
        test_config = TestConfig(
            tests_root=testdir,
            project_root_path=codedir.parent.resolve(),
            test_framework="pytest",
            tests_project_rootdir=testdir.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Check if the test for the nested class method is discovered
        assert len(discovered_tests) == 1
        discovered_test_file = next(
            iter(discovered_tests["mypackage.code.find_common_tags"])
        ).tests_in_file.test_file
        assert discovered_test_file.resolve() == test_file_path.resolve()


def test_unittest_discovery_with_pytest():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "calculator.py"
        code_file_content = """
class Calculator:
    def add(self, a, b):
        return a + b
"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file
        test_file_path = path_obj_tmpdirname / "test_calculator.py"
        test_file_content = """
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    def test_add(self):
        calc = Calculator()
        self.assertEqual(calc.add(2, 2), 4)
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Verify the unittest was discovered
        assert len(discovered_tests) == 1
        assert "calculator.Calculator.add" in discovered_tests
        assert len(discovered_tests["calculator.Calculator.add"]) == 1
        calculator_test = next(
            iter(discovered_tests["calculator.Calculator.add"])
        )
        assert (
            calculator_test.tests_in_file.test_file.resolve()
            == test_file_path.resolve()
        )
        assert calculator_test.tests_in_file.test_function == "test_add"


def test_unittest_discovery_with_pytest_parent_class():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "calculator.py"
        code_file_content = """
class Calculator:
    def add(self, a, b):
        return a + b
"""
        code_file_path.write_text(code_file_content)

        # Create a base test class file
        base_test_file_path = path_obj_tmpdirname / "base_test.py"
        base_test_content = """
import unittest

class BaseTestCase(unittest.TestCase):
    def setUp(self):
        self.setup_called = True

    def tearDown(self):
        self.setup_called = False

    def assert_setup_called(self):
        self.assertTrue(self.setup_called, "Setup was not called")
"""
        base_test_file_path.write_text(base_test_content)

        # Create a unittest test file that extends the base test
        test_file_path = path_obj_tmpdirname / "test_calculator.py"
        test_file_content = """
from base_test import BaseTestCase
from calculator import Calculator

class ExtendedTestCase(BaseTestCase):
    def setUp(self):
        super().setUp()
        self.calc = Calculator()

class TestCalculator(ExtendedTestCase):
    def test_add(self):
        self.assert_setup_called()
        self.assertEqual(self.calc.add(2, 2), 4)
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Verify the unittest was discovered
        assert len(discovered_tests) == 2
        assert "calculator.Calculator.add" in discovered_tests
        assert len(discovered_tests["calculator.Calculator.add"]) == 1
        calculator_test = next(
            iter(discovered_tests["calculator.Calculator.add"])
        )
        assert (
            calculator_test.tests_in_file.test_file.resolve()
            == test_file_path.resolve()
        )
        assert calculator_test.tests_in_file.test_function == "test_add"


def test_unittest_discovery_with_pytest_private():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "calculator.py"
        code_file_content = """
class Calculator:
    def add(self, a, b):
        return a + b
"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file with a private test method (prefixed with _)
        test_file_path = path_obj_tmpdirname / "test_calculator.py"
        test_file_content = """
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    def _test_add(self):  # Private test method should not be discovered
        calc = Calculator()
        self.assertEqual(calc.add(2, 2), 4)
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Verify no tests were discovered
        assert len(discovered_tests) == 0
        assert "calculator.Calculator.add" not in discovered_tests


def test_unittest_discovery_with_pytest_subtest():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "calculator.py"
        code_file_content = """
class Calculator:
    def add(self, a, b):
        return a + b
"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file with parameterized tests
        test_file_path = path_obj_tmpdirname / "test_calculator.py"
        test_file_content = """
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    def test_add_with_parameters(self):
        calc = Calculator()
        test_cases = [
            {"a": 2, "b": 2, "expected": 4},
            {"a": 0, "b": 0, "expected": 0},
            {"a": -1, "b": 1, "expected": 0},
            {"a": 10, "b": -5, "expected": 5}
        ]

        for case in test_cases:
            with self.subTest(a=case["a"], b=case["b"]):
                result = calc.add(case["a"], case["b"])
                self.assertEqual(result, case["expected"])
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Verify the unittest was discovered
        assert len(discovered_tests) == 1
        assert "calculator.Calculator.add" in discovered_tests
        assert len(discovered_tests["calculator.Calculator.add"]) == 1
        calculator_test = next(
            iter(discovered_tests["calculator.Calculator.add"])
        )
        assert (
            calculator_test.tests_in_file.test_file.resolve()
            == test_file_path.resolve()
        )
        assert (
            calculator_test.tests_in_file.test_function
            == "test_add_with_parameters"
        )


def test_unittest_discovery_with_pytest_fixture():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "topological_sort.py"
        code_file_content = """
import uuid
from collections import defaultdict


class Graph:
    def __init__(self, vertices: int):
        self.vertices=vertices

    def dummy_fn(self):
        return 1

    def topologicalSort(self):
        return self.vertices

"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file with parameterized tests
        test_file_path = path_obj_tmpdirname / "test_topological_sort.py"
        test_file_content = """
from topological_sort import Graph
import pytest

@pytest.fixture
def g():
    return Graph(6) 

def test_topological_sort(g):
    assert g.dummy_fn() == 1
    assert g.topologicalSort() == 6
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )
        fto = FunctionToOptimize(
            function_name="topologicalSort",
            file_path=code_file_path,
            parents=[FunctionParent(name="Graph", type="ClassDef")],
        )
        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(
            test_config, file_to_funcs_to_optimize={code_file_path: [fto]}
        )

        # Verify the unittest was discovered
        assert len(discovered_tests) == 2
        assert "topological_sort.Graph.topologicalSort" in discovered_tests
        assert (
            len(discovered_tests["topological_sort.Graph.topologicalSort"])
            == 1
        )
        tpsort_test = next(
            iter(discovered_tests["topological_sort.Graph.topologicalSort"])
        )
        assert (
            tpsort_test.tests_in_file.test_file.resolve()
            == test_file_path.resolve()
        )
        assert (
            tpsort_test.tests_in_file.test_function == "test_topological_sort"
        )


def test_unittest_discovery_with_pytest_class_fixture():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "router_file.py"
        code_file_content = """
from __future__ import annotations

import hashlib
import json

class Router:
    model_names: list
    cache_responses = False
    tenacity = None

    def __init__(  # noqa: PLR0915
            self,
            model_list = None,
    ) -> None:
        self.model_list = model_list
        self.model_id_to_deployment_index_map = {}
        self.model_name_to_deployment_indices = {}
    def _generate_model_id(self, model_group, litellm_params):
        # Optimized: Use list and join instead of string concatenation in loop
        # This avoids creating many temporary string objects (O(n) vs O(n²) complexity)
        parts = [model_group]
        for k, v in litellm_params.items():
            if isinstance(k, str):
                parts.append(k)
            elif isinstance(k, dict):
                parts.append(json.dumps(k))
            else:
                parts.append(str(k))

            if isinstance(v, str):
                parts.append(v)
            elif isinstance(v, dict):
                parts.append(json.dumps(v))
            else:
                parts.append(str(v))

        concat_str = "".join(parts)
        hash_object = hashlib.sha256(concat_str.encode())

        return hash_object.hexdigest()
    def _add_model_to_list_and_index_map(
            self, model, model_id  = None
    ) -> None:
        idx = len(self.model_list)
        self.model_list.append(model)

        # Update model_id index for O(1) lookup
        if model_id is not None:
            self.model_id_to_deployment_index_map[model_id] = idx
        elif model.get("model_info", {}).get("id") is not None:
            self.model_id_to_deployment_index_map[model["model_info"]["id"]] = idx

        # Update model_name index for O(1) lookup
        model_name = model.get("model_name")
        if model_name:
            if model_name not in self.model_name_to_deployment_indices:
                self.model_name_to_deployment_indices[model_name] = []
            self.model_name_to_deployment_indices[model_name].append(idx)

    def _build_model_id_to_deployment_index_map(self, model_list):
        # First populate the model_list
        self.model_list = []
        for _, model in enumerate(model_list):
            # Extract model_info from the model dict
            model_info = model.get("model_info", {})
            model_id = model_info.get("id")

            # If no ID exists, generate one using the same logic as set_model_list
            if model_id is None:
                model_name = model.get("model_name", "")
                litellm_params = model.get("litellm_params", {})
                model_id = self._generate_model_id(model_name, litellm_params)
                # Update the model_info in the original list
                if "model_info" not in model:
                    model["model_info"] = {}
                model["model_info"]["id"] = model_id

            self._add_model_to_list_and_index_map(model=model, model_id=model_id)

"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file with parameterized tests
        test_file_path = path_obj_tmpdirname / "test_router_file.py"
        test_file_content = """
import pytest

from router_file import Router


class TestRouterIndexManagement:
    @pytest.fixture
    def router(self):
        return Router(model_list=[])
    def test_build_model_id_to_deployment_index_map(self, router):
        model_list = [
            {
                "model_name": "gpt-3.5-turbo",
                "litellm_params": {"model": "gpt-3.5-turbo"},
                "model_info": {"id": "model-1"},
            },
            {
                "model_name": "gpt-4",
                "litellm_params": {"model": "gpt-4"},
                "model_info": {"id": "model-2"},
            },
        ]

        # Test: Build index from model list
        router._build_model_id_to_deployment_index_map(model_list)

        # Verify: model_list is populated
        assert len(router.model_list) == 2
        # Verify: model_id_to_deployment_index_map is correctly built
        assert router.model_id_to_deployment_index_map["model-1"] == 0
        assert router.model_id_to_deployment_index_map["model-2"] == 1
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",  # Using pytest framework to discover unittest tests
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )
        fto = FunctionToOptimize(
            function_name="_build_model_id_to_deployment_index_map",
            file_path=code_file_path,
            parents=[FunctionParent(name="Router", type="ClassDef")],
        )
        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(
            test_config, file_to_funcs_to_optimize={code_file_path: [fto]}
        )

        # Verify the unittest was discovered
        assert len(discovered_tests) == 1
        assert (
            "router_file.Router._build_model_id_to_deployment_index_map"
            in discovered_tests
        )
        assert (
            len(
                discovered_tests[
                    "router_file.Router._build_model_id_to_deployment_index_map"
                ]
            )
            == 1
        )
        router_test = next(
            iter(
                discovered_tests[
                    "router_file.Router._build_model_id_to_deployment_index_map"
                ]
            )
        )
        assert (
            router_test.tests_in_file.test_file.resolve()
            == test_file_path.resolve()
        )
        assert (
            router_test.tests_in_file.test_function
            == "test_build_model_id_to_deployment_index_map"
        )


def test_unittest_discovery_with_pytest_parameterized():
    with tempfile.TemporaryDirectory() as tmpdirname:
        path_obj_tmpdirname = Path(tmpdirname)

        # Create a simple code file
        code_file_path = path_obj_tmpdirname / "calculator.py"
        code_file_content = """
class Calculator:
    def add(self, a, b):
        return a + b

    def multiply(self, a, b):
        return a * b
"""
        code_file_path.write_text(code_file_content)

        # Create a unittest test file with different parameterized patterns
        test_file_path = path_obj_tmpdirname / "test_calculator.py"
        test_file_content = """
import unittest
from parameterized import parameterized
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    # Test with named parameters
    @parameterized.expand([
        ("positive_numbers", 2, 2, 4),
        ("zeros", 0, 0, 0),
        ("negative_and_positive", -1, 1, 0),
        ("negative_result", 10, -15, -5),
    ])
    def test_add(self, name, a, b, expected):
        calc = Calculator()
        result = calc.add(a, b)
        self.assertEqual(result, expected)

    # Test with unnamed parameters
    @parameterized.expand([
        (2, 3, 6),
        (0, 5, 0),
        (-2, 3, -6),
    ])
    def test_multiply(self, a, b, expected):
        calc = Calculator()
        result = calc.multiply(a, b)
        self.assertEqual(result, expected)

    # Test with mixed naming patterns
    @parameterized.expand([
        ("test with spaces", 1, 1, 2),
        ("test_with_underscores", 2, 2, 4),
        ("test.with.dots", 3, 3, 6),
        ("test-with-hyphens", 4, 4, 8),
    ])
    def test_add_mixed(self, name, a, b, expected):
        calc = Calculator()
        result = calc.add(a, b)
        self.assertEqual(result, expected)
"""
        test_file_path.write_text(test_file_content)

        # Configure test discovery
        test_config = TestConfig(
            tests_root=path_obj_tmpdirname,
            project_root_path=path_obj_tmpdirname,
            test_framework="pytest",
            tests_project_rootdir=path_obj_tmpdirname.parent,
        )

        # Discover tests
        discovered_tests, _, _ = discover_unit_tests(test_config)

        # Verify the basic structure
        assert (
            len(discovered_tests) == 2
        )  # Should have tests for both add and multiply
        assert "calculator.Calculator.add" in discovered_tests
        assert "calculator.Calculator.multiply" in discovered_tests


# Import Filtering Tests


def test_analyze_imports_direct_function_import():
    """Test that direct function imports are detected."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import target_function, other_function

def test_target():
    assert target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function", "missing_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_star_import():
    """Test that star imports trigger conservative processing."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import *

def test_something():
    assert something() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False

    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import *

def test_target():
    assert target_function() is True
"""
        test_file.group
        test_file.write_text(test_content)

        target_functions = {"mymodule.target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True

    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import *

def test_target():
    assert target_function_extended() is True
"""
        test_file.write_text(test_content)

        # Should not match - target_function != target_function_extended
        target_functions = {"mymodule.target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False

    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import *

def test_something():
    x = 42
    assert x == 42
"""
        test_file.write_text(test_content)

        target_functions = {"mymodule.target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False

    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import *

def test_something():
    message = "calling target_function"
    assert "target_function" in message
"""
        test_file.write_text(test_content)

        target_functions = {"mymodule.target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # String literals are ast.Constant nodes, not ast.Name nodes, so they don't match
        assert should_process is False

    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import target_function
from othermodule import *

def test_target():
    assert target_function() is True
    assert other_func() is True
"""
        test_file.write_text(test_content)

        target_functions = {
            "mymodule.target_function",
            "othermodule.other_func",
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_module_import():
    """Test module imports with function access patterns."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
import mymodule

def test_target():
    assert mymodule.target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_dynamic_import():
    """Test detection of dynamic imports."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
import importlib

def test_dynamic():
    module = importlib.import_module("mymodule")
    assert module.target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_builtin_import():
    """Test detection of __import__ calls."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
def test_builtin_import():
    module = __import__("mymodule")
    assert module.target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_no_matching_imports():
    """Test that files with no matching imports are filtered out."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from unrelated_module import unrelated_function

def test_unrelated():
    assert unrelated_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function", "another_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )
        assert should_process is False


def test_analyze_qualified_names():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from target_module import some_function

def test_target():
    assert some_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_module.some_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )
        assert should_process is True


def test_analyze_imports_syntax_error():
    """Test handling of files with syntax errors."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import target_function
def test_target(
    # Syntax error - missing closing parenthesis
    assert target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # Should be conservative with unparseable files
        assert should_process is True


def test_filter_test_files_by_imports():
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmpdir = Path(tmpdirname)

        # Create test file that imports target function
        relevant_test = tmpdir / "test_relevant.py"
        relevant_test.write_text("""
from mymodule import target_function

def test_target():
    assert target_function() is True
""")

        # Create test file that doesn't import target function
        irrelevant_test = tmpdir / "test_irrelevant.py"
        irrelevant_test.write_text("""
from othermodule import other_function

def test_other():
    assert other_function() is True
""")

        # Create test file with star import (should not be processed)
        star_test = tmpdir / "test_star.py"
        star_test.write_text("""
from mymodule import *

def test_star():
    assert something() is True
""")

        file_to_test_map = {
            relevant_test: [
                TestsInFile(
                    test_file=relevant_test,
                    test_function="test_target",
                    test_class=None,
                    test_type=TestType.EXISTING_UNIT_TEST,
                )
            ],
            irrelevant_test: [
                TestsInFile(
                    test_file=irrelevant_test,
                    test_function="test_other",
                    test_class=None,
                    test_type=TestType.EXISTING_UNIT_TEST,
                )
            ],
            star_test: [
                TestsInFile(
                    test_file=star_test,
                    test_function="test_star",
                    test_class=None,
                    test_type=TestType.EXISTING_UNIT_TEST,
                )
            ],
        }

        target_functions = {"target_function"}
        filtered_map = filter_test_files_by_imports(
            file_to_test_map, target_functions
        )

        # Should filter out irrelevant_test
        assert len(filtered_map) == 1
        assert relevant_test in filtered_map
        assert irrelevant_test not in filtered_map


def test_filter_test_files_no_target_functions():
    """Test that filtering is skipped when no target functions are provided."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmpdir = Path(tmpdirname)

        test_file = tmpdir / "test_example.py"
        test_file.write_text("def test_something(): pass")

        file_to_test_map = {
            test_file: [
                TestsInFile(
                    test_file=test_file,
                    test_function="test_something",
                    test_class=None,
                    test_type=TestType.EXISTING_UNIT_TEST,
                )
            ]
        }

        # No target functions provided
        filtered_map = filter_test_files_by_imports(file_to_test_map, set())

        # Should return original map unchanged
        assert filtered_map == file_to_test_map


def test_discover_unit_tests_with_import_filtering():
    """Test the full discovery process with import filtering."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmpdir = Path(tmpdirname)

        # Create a code file
        code_file = tmpdir / "mycode.py"
        code_file.write_text("""
def target_function():
    return True

def other_function():
    return False
""")

        # Create relevant test file
        relevant_test = tmpdir / "test_relevant.py"
        relevant_test.write_text("""
from mycode import target_function

def test_target():
    assert target_function() is True
""")

        # Create irrelevant test file
        irrelevant_test = tmpdir / "test_irrelevant.py"
        irrelevant_test.write_text("""
from mycode import other_function

def test_other():
    assert other_function() is False
""")

        # Configure test discovery
        test_config = TestConfig(
            tests_root=tmpdir,
            project_root_path=tmpdir,
            test_framework="pytest",
            tests_project_rootdir=tmpdir.parent,
        )

        all_tests, _, _ = discover_unit_tests(test_config)
        assert len(all_tests) == 2

        fto = FunctionToOptimize(
            function_name="target_function", file_path=code_file, parents=[]
        )

        filtered_tests, _, _ = discover_unit_tests(
            test_config, file_to_funcs_to_optimize={code_file: [fto]}
        )
        assert len(filtered_tests) >= 1
        assert "mycode.target_function" in filtered_tests


def test_analyze_imports_conditional_import():
    """Test detection of conditional imports within functions."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
def test_conditional():
    if some_condition:
        from mymodule import target_function
        assert target_function() is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_function_name_in_code():
    """Test detection of function names used directly in code."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
import mymodule

def test_indirect():
    func_name = "target_function"
    func = getattr(mymodule, func_name)
    # The analyzer should detect target_function usage
    result = target_function()
    assert result is True
"""
        test_file.write_text(test_content)

        target_functions = {"target_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_aliased_imports():
    """Test handling of aliased imports."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import target_function as tf, other_function as of

def test_aliased():
    assert tf() is True
    assert of() is False
"""
        test_file.write_text(test_content)

        target_functions = {"target_function", "missing_function"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_underscore_function_names():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from bubble_module import sort_function

def test_bubble():
    assert sort_function([3,1,2]) == [1,2,3]
"""
        test_file.write_text(test_content)

        target_functions = {"bubble_sort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False


def test_discover_unit_tests_filtering_different_modules():
    """Test import filtering with test files from completely different modules."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmpdir = Path(tmpdirname)

        # Create target code file
        target_file = tmpdir / "target_module.py"
        target_file.write_text("""
def target_function():
    return True
""")

        # Create unrelated code file
        unrelated_file = tmpdir / "unrelated_module.py"
        unrelated_file.write_text("""
def unrelated_function():
    return False
""")

        # Create test file that imports target function
        relevant_test = tmpdir / "test_target.py"
        relevant_test.write_text("""
from target_module import target_function

def test_target():
    assert target_function() is True
""")

        # Create test file that imports unrelated function
        irrelevant_test = tmpdir / "test_unrelated.py"
        irrelevant_test.write_text("""
from unrelated_module import unrelated_function

def test_unrelated():
    assert unrelated_function() is False
""")

        # Configure test discovery
        test_config = TestConfig(
            tests_root=tmpdir,
            project_root_path=tmpdir,
            test_framework="pytest",
            tests_project_rootdir=tmpdir.parent,
        )

        # Test without filtering
        all_tests, _, _ = discover_unit_tests(test_config)
        assert len(all_tests) == 2  # Should find both functions

        fto = FunctionToOptimize(
            function_name="target_function", file_path=target_file, parents=[]
        )

        filtered_tests, _, _ = discover_unit_tests(
            test_config, file_to_funcs_to_optimize={target_file: [fto]}
        )
        assert len(filtered_tests) == 1
        assert "target_module.target_function" in filtered_tests
        assert "unrelated_module.unrelated_function" not in filtered_tests


def test_analyze_imports_aliased_class_method():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from pydantic_ai.profiles.google import (
    GoogleJsonSchemaTransformer as pydantic_ai_profiles_google_GoogleJsonSchemaTransformer,
)

def test_target():
    ret = pydantic_ai_profiles_google_GoogleJsonSchemaTransformer.transform(*args, **kwargs)
    assert ret is not None
"""
        test_file.write_text(test_content)

        target_functions = {"GoogleJsonSchemaTransformer.transform"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_method():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from code_to_optimize.topological_sort import Graph


def test_topological_sort():
    g = Graph(6)
    g.addEdge(5, 2)
    g.addEdge(5, 0)
    g.addEdge(4, 0)
    g.addEdge(4, 1)
    g.addEdge(2, 3)
    g.addEdge(3, 1)

    assert g.topologicalSort()[0] == [5, 4, 2, 3, 1, 0]
"""
        test_file.write_text(test_content)

        target_functions = {"Graph.topologicalSort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_fixture():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from code_to_optimize.topological_sort import Graph
import pytest

@pytest.fixture
def g():
    return Graph(6) 

def test_topological_sort(g):
    g.addEdge(5, 2)
    g.addEdge(5, 0)
    g.addEdge(4, 0)
    g.addEdge(4, 1)
    g.addEdge(2, 3)
    g.addEdge(3, 1)

    assert g.topologicalSort()[0] == [5, 4, 2, 3, 1, 0]
"""
        test_file.write_text(test_content)

        target_functions = {"Graph.topologicalSort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_class_fixture():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
import pytest

from router_file import Router


class TestRouterIndexManagement:
    @pytest.fixture
    def router(self):
        return Router(model_list=[])
    def test_build_model_id_to_deployment_index_map(self, router):
        model_list = [
            {
                "model_name": "gpt-3.5-turbo",
                "litellm_params": {"model": "gpt-3.5-turbo"},
                "model_info": {"id": "model-1"},
            },
            {
                "model_name": "gpt-4",
                "litellm_params": {"model": "gpt-4"},
                "model_info": {"id": "model-2"},
            },
        ]

        # Test: Build index from model list
        router._build_model_id_to_deployment_index_map(model_list)

        # Verify: model_list is populated
        assert len(router.model_list) == 2
        # Verify: model_id_to_deployment_index_map is correctly built
        assert router.model_id_to_deployment_index_map["model-1"] == 0
        assert router.model_id_to_deployment_index_map["model-2"] == 1
"""
        test_file.write_text(test_content)

        target_functions = {"Router._build_model_id_to_deployment_index_map"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_aliased_class_method_negative():
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from pydantic_ai.profiles.google import (
    GoogleJsonSchemaTransformer as pydantic_ai_profiles_google_GoogleJsonSchemaTransformer,
)

def test_target():
    ret = pydantic_ai_profiles_google_GoogleJsonSchemaTransformer.validate(*args, **kwargs)
    assert ret is not None
"""
        test_file.write_text(test_content)

        # Looking for transform but code uses validate - should not match
        target_functions = {"GoogleJsonSchemaTransformer.transform"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False


def test_analyze_imports_class_with_multiple_methods():
    """Test importing a class when looking for multiple methods of that class."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import MyClass

def test_methods():
    obj = MyClass()
    assert obj.method1() is True
    assert obj.method2() is False
    assert obj.method3() == 42
"""
        test_file.write_text(test_content)

        # Looking for multiple methods of the same class
        target_functions = {
            "MyClass.method1",
            "MyClass.method2",
            "MyClass.method3",
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_class_method_with_nested_classes():
    """Test importing nested classes and their methods."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import OuterClass

def test_nested():
    outer = OuterClass()
    inner = outer.InnerClass()
    assert inner.inner_method() is True
"""
        test_file.write_text(test_content)

        # This would require more complex analysis of nested classes
        # Currently only direct class.method patterns are supported
        target_functions = {"OuterClass.InnerClass.inner_method"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # Our fix detects OuterClass from OuterClass.InnerClass.inner_method
        # This is overly broad but conservative (better to include than exclude)
        assert should_process is True


def test_analyze_imports_class_method_partial_match():
    """Test that partial class names don't match incorrectly."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import GraphBuilder

def test_builder():
    builder = GraphBuilder()
    assert builder.build() is not None
"""
        test_file.write_text(test_content)

        # Looking for Graph.topologicalSort, not GraphBuilder
        target_functions = {"Graph.topologicalSort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False


def test_analyze_imports_class_method_with_inheritance():
    """Test importing a child class when looking for parent class methods."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import ChildClass

def test_inherited():
    child = ChildClass()
    # Assuming ChildClass inherits from ParentClass
    assert child.parent_method() is True
"""
        test_file.write_text(test_content)

        # Looking for parent class method, but only child is imported
        target_functions = {"ParentClass.parent_method"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False


def test_analyze_imports_class_static_and_class_methods():
    """Test importing a class and calling static/class methods."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import MyClass

def test_static_and_class_methods():
    # Static method call
    assert MyClass.static_method() is True

    # Class method call
    result = MyClass.class_method()
    assert result == "expected"

    # Instance method call
    obj = MyClass()
    assert obj.instance_method() is False
"""
        test_file.write_text(test_content)

        target_functions = {
            "MyClass.static_method",
            "MyClass.class_method",
            "MyClass.instance_method",
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_multiple_classes_same_module():
    """Test importing multiple classes from the same module."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import ClassA, ClassB, ClassC

def test_multiple_classes():
    a = ClassA()
    b = ClassB()
    c = ClassC()

    assert a.methodA() is True
    assert b.methodB() is False
    assert c.methodC() == 42
"""
        test_file.write_text(test_content)

        # Looking for methods from different classes
        target_functions = {
            "ClassA.methodA",
            "ClassB.methodB",
            "ClassD.methodD",
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True  # ClassA and ClassB are imported


def test_analyze_imports_class_method_case_sensitive():
    """Test that class name matching is case-sensitive."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import graph

def test_lowercase():
    g = graph()
    assert g.topologicalSort() is not None
"""
        test_file.write_text(test_content)

        # Looking for Graph (capital G), but imported graph (lowercase)
        target_functions = {"Graph.topologicalSort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is False


def test_analyze_imports_class_from_submodule():
    """Test importing a class from a submodule."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from package.subpackage.module import MyClass

def test_submodule_class():
    obj = MyClass()
    assert obj.my_method() is True
"""
        test_file.write_text(test_content)

        target_functions = {"MyClass.my_method"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_aliased_class_with_methods():
    """Test importing a class with an alias and looking for its methods."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import Graph as G

def test_aliased_class():
    graph = G(10)
    result = graph.topologicalSort()
    assert result is not None
"""
        test_file.write_text(test_content)

        target_functions = {"Graph.topologicalSort"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_class_property_access():
    """Test importing a class and accessing properties (not methods)."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import MyClass

def test_properties():
    obj = MyClass()
    # Accessing properties, not methods
    assert obj.size == 10
    assert obj.name == "test"
"""
        test_file.write_text(test_content)

        # Looking for methods, but only properties are accessed
        # Our fix conservatively includes when class is imported
        target_functions = {"MyClass.get_size", "MyClass.get_name"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True  # Conservative approach


def test_analyze_imports_class_constructor_params():
    """Test class import when looking for __init__ method."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import MyClass

def test_constructor():
    # Testing the constructor
    obj1 = MyClass()
    obj2 = MyClass(10)
    obj3 = MyClass(size=20, name="test")

    assert obj1 is not None
    assert obj2 is not None
    assert obj3 is not None
"""
        test_file.write_text(test_content)

        # __init__ is a special method that would require additional logic
        target_functions = {"MyClass.__init__"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # Our fix now detects MyClass from MyClass.__init__
        assert should_process is True


def test_analyze_imports_class_method_chaining():
    """Test method chaining on imported classes."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import Builder

def test_chaining():
    result = Builder().add_item("a").add_item("b").build()
    assert result is not None
"""
        test_file.write_text(test_content)

        # Method chaining requires tracking object types through chained calls
        target_functions = {"Builder.add_item", "Builder.build"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # Currently detects Builder import and methods
        assert should_process is True


def test_analyze_imports_mixed_function_and_class_imports():
    """Test mixed imports of functions and classes from the same module."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from mymodule import MyClass, standalone_function, AnotherClass

def test_mixed():
    # Using class method
    obj = MyClass()
    assert obj.method() is True

    # Using standalone function
    assert standalone_function() is False

    # Using another class
    other = AnotherClass()
    assert other.other_method() == 42
"""
        test_file.write_text(test_content)

        target_functions = {
            "MyClass.method",
            "standalone_function",
            "YetAnotherClass.method",
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert (
            should_process is True
        )  # MyClass.method and standalone_function are imported


def test_analyze_imports_class_with_module_prefix():
    """Test looking for fully qualified class methods."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from code_to_optimize.topological_sort import Graph

def test_fully_qualified():
    g = Graph(5)
    assert g.topologicalSort() == [4, 3, 2, 1, 0]
"""
        test_file.write_text(test_content)

        # Looking with full module path would require more complex module resolution
        target_functions = {
            "code_to_optimize.topological_sort.Graph.topologicalSort"
        }
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # Currently not supported - would need to match module path with imports
        assert should_process is False


def test_analyze_imports_reimport_in_function():
    """Test class import inside a function."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
def test_local_import():
    from mymodule import MyClass
    obj = MyClass()
    assert obj.method() is True
"""
        test_file.write_text(test_content)

        target_functions = {"MyClass.method"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        assert should_process is True


def test_analyze_imports_class_in_type_annotation():
    """Test class used only in type annotations."""
    with tempfile.TemporaryDirectory() as tmpdirname:
        test_file = Path(tmpdirname) / "test_example.py"
        test_content = """
from typing import Optional
from mymodule import MyClass

def helper_function(obj: Optional[MyClass]) -> bool:
    if obj:
        return obj.method()
    return False

def test_with_type_annotation():
    # MyClass is imported but only used in type annotation
    result = helper_function(None)
    assert result is False
"""
        test_file.write_text(test_content)

        target_functions = {"MyClass.method"}
        should_process = analyze_imports_in_test_file(
            test_file, target_functions
        )

        # MyClass is imported, so class.method pattern should match
        assert should_process is True


def test_discover_unit_tests_caching():
    tests_root = Path(__file__).parent.resolve() / "tests"
    project_root_path = tests_root.parent.resolve()

    test_config = TestConfig(
        tests_root=tests_root,
        project_root_path=project_root_path,
        test_framework="pytest",
        tests_project_rootdir=project_root_path,
        use_cache=False,
    )

    (
        non_cached_function_to_tests,
        non_cached_num_discovered_tests,
        non_cached_num_discovered_replay_tests,
    ) = discover_unit_tests(test_config)
    cache_config = TestConfig(
        tests_root=tests_root,
        project_root_path=project_root_path,
        test_framework="pytest",
        tests_project_rootdir=project_root_path,
        use_cache=True,
    )
    tests, num_discovered_tests, num_discovered_replay_tests = (
        discover_unit_tests(cache_config)
    )

    assert non_cached_num_discovered_tests == num_discovered_tests
    assert non_cached_function_to_tests == tests
    assert (
        non_cached_num_discovered_replay_tests == num_discovered_replay_tests
    )
