"""Comprehensive unit tests for TestResults.file_to_no_of_tests method."""

from collections import Counter
from pathlib import Path

from codeflash_python.test_discovery.models import TestType
from codeflash_python.testing.models import (
    FunctionTestInvocation,
    InvocationId,
    TestResults,
)


class TestFileToNoOfTests:
    """Test suite for TestResults.file_to_no_of_tests method."""

    def test_empty_test_results(self):
        """Test with empty test results."""
        test_results = TestResults()
        counter = test_results.file_to_no_of_tests([])
        assert counter == Counter()
        assert len(counter) == 0

    def test_empty_test_functions_to_remove(self):
        """Test with empty list of test functions to remove."""
        test_results = TestResults()
        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name="test_function",
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=Path("/tmp/test_file.py"),
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )
        counter = test_results.file_to_no_of_tests([])
        assert counter == Counter({Path("/tmp/test_file.py"): 1})

    def test_single_test_not_removed(self):
        """Test with a single test that should not be removed."""
        test_results = TestResults()
        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name="test_keep",
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=Path("/tmp/test_file.py"),
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )
        counter = test_results.file_to_no_of_tests(["test_remove"])
        assert counter == Counter({Path("/tmp/test_file.py"): 1})

    def test_single_test_removed(self):
        """Test with a single test that should be removed."""
        test_results = TestResults()
        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name="test_remove",
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=Path("/tmp/test_file.py"),
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )
        counter = test_results.file_to_no_of_tests(["test_remove"])
        assert counter == Counter()

    def test_multiple_tests_same_file(self):
        """Test with multiple tests in the same file."""
        test_results = TestResults()
        for i in range(5):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module",
                        test_class_name="TestClass",
                        test_function_name=f"test_func_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=Path("/tmp/test_file.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )
        counter = test_results.file_to_no_of_tests([])
        assert counter == Counter({Path("/tmp/test_file.py"): 5})

    def test_multiple_tests_different_files(self):
        """Test with multiple tests in different files."""
        test_results = TestResults()
        files = [Path(f"/tmp/test_file_{i}.py") for i in range(3)]
        for i, file_path in enumerate(files):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path=f"test.module{i}",
                        test_class_name="TestClass",
                        test_function_name=f"test_func_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=file_path,
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )
        counter = test_results.file_to_no_of_tests([])
        expected = Counter({files[0]: 1, files[1]: 1, files[2]: 1})
        assert counter == expected

    def test_mixed_test_types(self):
        """Test with different test types - only GENERATED_REGRESSION should be counted."""
        test_results = TestResults()
        test_types = [
            TestType.EXISTING_UNIT_TEST,
            TestType.INSPIRED_REGRESSION,
            TestType.GENERATED_REGRESSION,
            TestType.REPLAY_TEST,
            TestType.CONCOLIC_COVERAGE_TEST,
            TestType.INIT_STATE_TEST,
        ]

        for i, test_type in enumerate(test_types):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module",
                        test_class_name="TestClass",
                        test_function_name=f"test_func_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=Path(f"/tmp/test_file_{i}.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=test_type,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests([])
        # Only the GENERATED_REGRESSION test should be counted
        assert counter == Counter({Path("/tmp/test_file_2.py"): 1})

    def test_partial_removal(self):
        """Test removing some but not all tests from a file."""
        test_results = TestResults()
        test_names = [
            "test_keep_1",
            "test_remove_1",
            "test_keep_2",
            "test_remove_2",
        ]

        for name in test_names:
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module",
                        test_class_name="TestClass",
                        test_function_name=name,
                        function_getting_tested="target_func",
                        iteration_id=name,
                    ),
                    file_name=Path("/tmp/test_file.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests(
            ["test_remove_1", "test_remove_2"]
        )
        assert counter == Counter(
            {Path("/tmp/test_file.py"): 2}
        )  # Only test_keep_1 and test_keep_2

    def test_none_test_function_name(self):
        """Test with None test_function_name."""
        test_results = TestResults()
        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name=None,
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=Path("/tmp/test_file.py"),
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )
        # None should not match any string in test_functions_to_remove
        counter = test_results.file_to_no_of_tests(["test_remove"])
        assert counter == Counter({Path("/tmp/test_file.py"): 1})

    def test_duplicate_file_paths(self):
        """Test counting with duplicate file paths across multiple tests."""
        test_results = TestResults()
        file_path = Path("/tmp/test_file.py")

        # Add multiple tests with the same file path
        for i in range(3):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module",
                        test_class_name="TestClass",
                        test_function_name=f"test_func_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=file_path,
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests([])
        assert counter == Counter({file_path: 3})

    def test_complex_scenario(self):
        """Test complex scenario with mixed conditions."""
        test_results = TestResults()

        # File 1: Mix of test types
        for i, test_type in enumerate(
            [TestType.GENERATED_REGRESSION, TestType.EXISTING_UNIT_TEST]
        ):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module1",
                        test_class_name="TestClass",
                        test_function_name=f"test_file1_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=Path("/tmp/file1.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=test_type,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        # File 2: Tests to be removed and kept
        for name in ["test_keep", "test_remove"]:
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module2",
                        test_class_name="TestClass",
                        test_function_name=name,
                        function_getting_tested="target_func",
                        iteration_id=name,
                    ),
                    file_name=Path("/tmp/file2.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        # File 3: All GENERATED_REGRESSION tests
        for i in range(3):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module3",
                        test_class_name="TestClass",
                        test_function_name=f"test_file3_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=Path("/tmp/file3.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests(["test_remove"])
        expected = Counter(
            {
                Path("/tmp/file1.py"): 1,  # Only 1 GENERATED_REGRESSION test
                Path(
                    "/tmp/file2.py"
                ): 1,  # Only test_keep (test_remove is excluded)
                Path("/tmp/file3.py"): 3,  # All 3 tests
            }
        )
        assert counter == expected

    def test_case_sensitivity(self):
        """Test that function name matching is case-sensitive."""
        test_results = TestResults()
        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name="Test_Function",
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=Path("/tmp/test_file.py"),
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )

        # Should not remove because case doesn't match
        counter = test_results.file_to_no_of_tests(["test_function"])
        assert counter == Counter({Path("/tmp/test_file.py"): 1})

        # Should remove with correct case
        counter = test_results.file_to_no_of_tests(["Test_Function"])
        assert counter == Counter()

    def test_windows_paths(self):
        """Test with Windows-style paths."""
        test_results = TestResults()
        windows_path = Path("C:\\Users\\test\\test_file.py")

        test_results.add(
            FunctionTestInvocation(
                id=InvocationId(
                    test_module_path="test.module",
                    test_class_name="TestClass",
                    test_function_name="test_func",
                    function_getting_tested="target_func",
                    iteration_id="1",
                ),
                file_name=windows_path,
                did_pass=True,
                runtime=100,
                test_framework="pytest",
                test_type=TestType.GENERATED_REGRESSION,
                return_value=None,
                timed_out=False,
                loop_index=1,
            )
        )

        counter = test_results.file_to_no_of_tests([])
        assert counter == Counter({windows_path: 1})

    def test_relative_and_absolute_paths(self):
        """Test with both relative and absolute paths."""
        test_results = TestResults()
        paths = [
            Path("/absolute/path/test.py"),
            Path("relative/path/test.py"),
            Path("./current/dir/test.py"),
            Path("../parent/dir/test.py"),
        ]

        for i, path in enumerate(paths):
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path=f"test.module{i}",
                        test_class_name="TestClass",
                        test_function_name=f"test_func_{i}",
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=path,
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests([])
        expected = Counter(dict.fromkeys(paths, 1))
        assert counter == expected

    def test_large_removal_list(self):
        """Test with a large list of functions to remove."""
        test_results = TestResults()
        num_tests = 100
        removal_list = [f"test_remove_{i}" for i in range(50)]

        for i in range(num_tests):
            test_name = f"test_remove_{i}" if i < 50 else f"test_keep_{i}"
            test_results.add(
                FunctionTestInvocation(
                    id=InvocationId(
                        test_module_path="test.module",
                        test_class_name="TestClass",
                        test_function_name=test_name,
                        function_getting_tested="target_func",
                        iteration_id=str(i),
                    ),
                    file_name=Path("/tmp/test_file.py"),
                    did_pass=True,
                    runtime=100,
                    test_framework="pytest",
                    test_type=TestType.GENERATED_REGRESSION,
                    return_value=None,
                    timed_out=False,
                    loop_index=1,
                )
            )

        counter = test_results.file_to_no_of_tests(removal_list)
        assert counter == Counter(
            {Path("/tmp/test_file.py"): 50}
        )  # 50 kept, 50 removed
