"""Base types and protocol for multi-language support in Codeflash.

This module defines the core abstractions that all language implementations must follow.
The LanguageSupport protocol defines the interface that each language must implement,
while FunctionToOptimize is the canonical representation of functions across all languages.
"""

from __future__ import annotations

import fnmatch
import re
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable

if TYPE_CHECKING:
    import ast
    from collections.abc import Callable, Iterable, Sequence
    from pathlib import Path

    from codeflash.discovery.functions_to_optimize import FunctionToOptimize
    from codeflash.models.call_graph import CallGraph
    from codeflash.models.models import FunctionSource, GeneratedTestsList, InvocationId, ValidCode
    from codeflash.verification.verification_utils import TestConfig

from codeflash.languages.language_enum import Language
from codeflash.models.function_types import FunctionParent

# Backward compatibility aliases - ParentInfo is now FunctionParent
ParentInfo = FunctionParent


# Lazy import for FunctionInfo to avoid circular imports
# This allows `from codeflash.languages.base import FunctionInfo` to work at runtime
def __getattr__(name: str) -> Any:
    if name == "FunctionInfo":
        from codeflash.discovery.functions_to_optimize import FunctionToOptimize

        return FunctionToOptimize
    msg = f"module {__name__!r} has no attribute {name!r}"
    raise AttributeError(msg)


@dataclass(frozen=True)
class IndexResult:
    file_path: Path
    cached: bool
    num_edges: int
    edges: tuple[tuple[str, str, bool], ...]  # (caller_qn, callee_name, is_cross_file)
    cross_file_edges: int
    error: bool


@dataclass(frozen=True)
class SetupError:
    message: str
    should_abort: bool


@dataclass
class HelperFunction:
    """A helper function that is a dependency of the target function.

    Helper functions are functions called by the target function that are
    within the same module/project (not external libraries).

    Attributes:
        name: The simple function name.
        qualified_name: Full qualified name including parent scopes.
        file_path: Path to the file containing the helper.
        source_code: The source code of the helper function.
        start_line: Starting line number.
        end_line: Ending line number.

    """

    name: str
    qualified_name: str
    file_path: Path
    source_code: str
    start_line: int
    end_line: int


@dataclass
class CodeContext:
    """Code context extracted for optimization.

    Contains the target function code and all relevant dependencies
    needed for the AI to understand and optimize the function.

    Attributes:
        target_code: Source code of the function to optimize.
        target_file: Path to the file containing the target function.
        helper_functions: List of helper functions called by the target.
        read_only_context: Additional context code (read-only dependencies).
        imports: List of import statements needed.
        language: The programming language.

    """

    target_code: str
    target_file: Path
    helper_functions: list[HelperFunction] = field(default_factory=list)
    read_only_context: str = ""
    imported_type_skeletons: str = ""
    imports: list[str] = field(default_factory=list)
    language: Language = Language.PYTHON


@dataclass
class TestInfo:
    """Information about a test that exercises a function.

    Attributes:
        test_name: Name of the test function.
        test_file: Path to the test file.
        test_class: Name of the test class, if any.

    """

    test_name: str
    test_file: Path
    test_class: str | None = None
    is_replay: bool = False

    @property
    def full_test_path(self) -> str:
        """Get full test path in pytest format (file::class::function)."""
        file_path = self.test_file.as_posix()
        if self.test_class:
            return f"{file_path}::{self.test_class}::{self.test_name}"
        return f"{file_path}::{self.test_name}"


@dataclass
class TestResult:
    """Language-agnostic test result.

    Captures the outcome of running a single test, including timing
    and behavioral data for equivalence checking.

    Attributes:
        test_name: Name of the test function.
        test_file: Path to the test file.
        passed: Whether the test passed.
        runtime_ns: Execution time in nanoseconds.
        return_value: The return value captured from the test.
        stdout: Standard output captured during test execution.
        stderr: Standard error captured during test execution.
        error_message: Error message if the test failed.

    """

    test_name: str
    test_file: Path
    passed: bool
    runtime_ns: int | None = None
    return_value: Any = None
    stdout: str = ""
    stderr: str = ""
    error_message: str | None = None


@dataclass
class FunctionFilterCriteria:
    """Criteria for filtering which functions to discover.

    Attributes:
        include_patterns: Glob patterns for functions to include.
        exclude_patterns: Glob patterns for functions to exclude.
        require_return: Only include functions with return statements.
        include_async: Include async functions.
        include_methods: Include class methods.
        min_lines: Minimum number of lines in the function.
        max_lines: Maximum number of lines in the function.

    """

    include_patterns: list[str] = field(default_factory=list)
    exclude_patterns: list[str] = field(default_factory=list)
    require_return: bool = True
    require_export: bool = True
    include_async: bool = True
    include_methods: bool = True
    min_lines: int | None = None
    max_lines: int | None = None

    def __post_init__(self) -> None:
        """Pre-compile regex patterns from glob patterns for faster matching."""
        self._include_regexes = [re.compile(fnmatch.translate(p)) for p in self.include_patterns]
        self._exclude_regexes = [re.compile(fnmatch.translate(p)) for p in self.exclude_patterns]

    def matches_include_patterns(self, name: str) -> bool:
        """Check if name matches any include pattern."""
        if not self._include_regexes:
            return True
        return any(regex.match(name) for regex in self._include_regexes)

    def matches_exclude_patterns(self, name: str) -> bool:
        """Check if name matches any exclude pattern."""
        if not self._exclude_regexes:
            return False
        return any(regex.match(name) for regex in self._exclude_regexes)


@dataclass
class ReferenceInfo:
    """Information about a reference (call site) to a function.

    This class captures information about where a function is called
    from, including the file, line number, context, and caller function.

    Attributes:
        file_path: Path to the file containing the reference.
        line: Line number (1-indexed).
        column: Column number (0-indexed).
        end_line: End line number (1-indexed).
        end_column: End column number (0-indexed).
        context: The line of code containing the reference.
        reference_type: Type of reference ("call", "callback", "memoized", "import", "reexport").
        import_name: Name used to import the function (may differ from original).
        caller_function: Name of the function containing this reference (or None for module-level).

    """

    file_path: Path
    line: int
    column: int
    end_line: int
    end_column: int
    context: str
    reference_type: str
    import_name: str | None
    caller_function: str | None = None


@runtime_checkable
class DependencyResolver(Protocol):
    """Protocol for language-specific dependency resolution.

    Implementations analyze source files to discover call-graph edges
    between functions so the optimizer can extract richer context.
    """

    def build_index(self, file_paths: Iterable[Path], on_progress: Callable[[IndexResult], None] | None = None) -> None:
        """Pre-index a batch of files."""
        ...

    def get_callees(
        self, file_path_to_qualified_names: dict[Path, set[str]]
    ) -> tuple[dict[Path, set[FunctionSource]], list[FunctionSource]]:
        """Return callees for the given functions."""
        ...

    def count_callees_per_function(
        self, file_path_to_qualified_names: dict[Path, set[str]]
    ) -> dict[tuple[Path, str], int]:
        """Return the number of callees for each (file_path, qualified_name) pair."""
        ...

    def get_call_graph(
        self, file_path_to_qualified_names: dict[Path, set[str]], *, include_metadata: bool = False
    ) -> CallGraph:
        """Return a CallGraph with full caller→callee edges for the given functions."""
        ...

    def close(self) -> None:
        """Release resources (e.g. database connections)."""
        ...


@runtime_checkable
class LanguageSupport(Protocol):
    """Protocol defining what a language implementation must provide.

    All language-specific implementations (Python, JavaScript, etc.) must
    implement this protocol. The protocol defines the interface for:
    - Function discovery
    - Code context extraction
    - Code transformation (replacement)
    - Test execution
    - Test discovery
    - Instrumentation for tracing

    Example:
        class PythonSupport(LanguageSupport):
            @property
            def language(self) -> Language:
                return Language.PYTHON

            def discover_functions(self, source: str, file_path: Path, ...) -> list[FunctionInfo]:
                # Python-specific implementation using LibCST
                ...

    """

    # === Properties ===

    @property
    def language(self) -> Language:
        """The language this implementation supports."""
        ...

    @property
    def file_extensions(self) -> tuple[str, ...]:
        """File extensions supported by this language.

        Returns:
            Tuple of extensions with leading dots (e.g., (".py",) for Python).

        """
        ...

    @property
    def default_file_extension(self) -> str:
        """Default file extension for this language."""
        ...

    @property
    def test_framework(self) -> str:
        """Primary test framework name.

        Returns:
            Test framework identifier (e.g., "pytest", "jest").

        """
        ...

    @property
    def comment_prefix(self) -> str:
        """Like # or //."""
        ...

    @property
    def dir_excludes(self) -> frozenset[str]:
        """Directory name patterns to skip during file discovery.

        Supports glob wildcards: "name" for exact, "prefix*" for startswith, "*suffix" for endswith.
        """
        ...

    @property
    def language_version(self) -> str | None:
        """The detected language version (e.g., "17" for Java, "ES2022" for JS)."""
        ...

    @property
    def valid_test_frameworks(self) -> tuple[str, ...]:
        """Valid test frameworks for this language."""
        ...

    @property
    def test_result_serialization_format(self) -> str:
        """How test return values are serialized: "pickle" or "json"."""
        return "pickle"

    def parse_test_xml(
        self, test_xml_file_path: Path, test_files: Any, test_config: Any, run_result: Any = None
    ) -> Any:
        """Parse JUnit XML test results with language-specific timing markers."""
        ...

    def load_coverage(
        self,
        coverage_database_file: Path,
        function_name: str,
        code_context: Any,
        source_file: Path,
        coverage_config_file: Path | None = None,
    ) -> Any:
        """Load coverage data from language-specific format.

        Returns a CoverageData instance.
        """
        ...

    # === Discovery ===

    def discover_functions(
        self, source: str, file_path: Path, filter_criteria: FunctionFilterCriteria | None = None
    ) -> list[FunctionToOptimize]:
        """Find all optimizable functions in source code.

        Args:
            source: Source code to analyze.
            file_path: Path to the source file (used for context and language detection).
            filter_criteria: Optional criteria to filter functions.

        Returns:
            List of FunctionToOptimize objects for discovered functions.

        """
        ...

    def discover_tests(
        self, test_root: Path, source_functions: Sequence[FunctionToOptimize]
    ) -> dict[str, list[TestInfo]]:
        """Map source functions to their tests via static analysis.

        Args:
            test_root: Root directory containing tests.
            source_functions: Functions to find tests for.

        Returns:
            Dict mapping qualified function names to lists of TestInfo.

        """
        ...

    # === Code Analysis ===

    def extract_code_context(self, function: FunctionToOptimize, project_root: Path, module_root: Path) -> CodeContext:
        """Extract function code and its dependencies.

        Args:
            function: The function to extract context for.
            project_root: Root of the project.
            module_root: Root of the module containing the function.

        Returns:
            CodeContext with target code and dependencies.

        """
        ...

    def find_helper_functions(self, function: FunctionToOptimize, project_root: Path) -> list[HelperFunction]:
        """Find helper functions called by the target function.

        Args:
            function: The target function to analyze.
            project_root: Root of the project.

        Returns:
            List of HelperFunction objects.

        """
        ...

    def find_references(
        self, function: FunctionToOptimize, project_root: Path, tests_root: Path | None = None, max_files: int = 500
    ) -> list[ReferenceInfo]:
        """Find all references (call sites) to a function across the codebase.

        This method finds all places where a function is called, including:
        - Direct calls
        - Callbacks (passed to other functions)
        - Memoized versions
        - Re-exports

        Args:
            function: The function to find references for.
            project_root: Root of the project to search.
            tests_root: Root of tests directory (references in tests are excluded).
            max_files: Maximum number of files to search.

        Returns:
            List of ReferenceInfo objects describing each reference location.

        """
        ...

    # === Code Transformation ===

    def replace_function(self, source: str, function: FunctionToOptimize, new_source: str) -> str:
        """Replace a function in source code with new implementation.

        Args:
            source: Original source code.
            function: FunctionToOptimize identifying the function to replace.
            new_source: New function source code.

        Returns:
            Modified source code with function replaced.

        """
        ...

    def format_code(self, source: str, file_path: Path | None = None) -> str:
        """Format code using language-specific formatter.

        Args:
            source: Source code to format.
            file_path: Optional file path for context.

        Returns:
            Formatted source code.

        """
        ...

    # === Test Execution ===

    def run_tests(
        self, test_files: Sequence[Path], cwd: Path, env: dict[str, str], timeout: int
    ) -> tuple[list[TestResult], Path]:
        """Run tests and return results.

        Args:
            test_files: Paths to test files to run.
            cwd: Working directory for test execution.
            env: Environment variables.
            timeout: Maximum execution time in seconds.

        Returns:
            Tuple of (list of TestResults, path to JUnit XML).

        """
        ...

    def parse_test_results(self, junit_xml_path: Path, stdout: str) -> list[TestResult]:
        """Parse test results from JUnit XML and stdout.

        Args:
            junit_xml_path: Path to JUnit XML results file.
            stdout: Standard output from test execution.

        Returns:
            List of TestResult objects.

        """
        ...

    # === Instrumentation ===

    def instrument_for_behavior(self, source: str, functions: Sequence[FunctionToOptimize]) -> str:
        """Add behavior instrumentation to capture inputs/outputs.

        Args:
            source: Source code to instrument.
            functions: Functions to add behavior capture.

        Returns:
            Instrumented source code.

        """
        ...

    def instrument_for_benchmarking(self, test_source: str, target_function: FunctionToOptimize) -> str:
        """Add timing instrumentation to test code.

        Args:
            test_source: Test source code to instrument.
            target_function: Function being benchmarked.

        Returns:
            Instrumented test source code.

        """
        ...

    # === Validation ===

    def validate_syntax(self, source: str, file_path: Path | None = None) -> bool:
        """Check if source code is syntactically valid.

        Args:
            source: Source code to validate.
            file_path: Optional file path for parser selection (e.g., .tsx vs .ts).

        Returns:
            True if valid, False otherwise.

        """
        ...

    def normalize_code(self, source: str) -> str:
        """Normalize code for deduplication.

        Removes comments, normalizes whitespace, etc. to allow
        comparison of semantically equivalent code.

        Args:
            source: Source code to normalize.

        Returns:
            Normalized source code.

        """
        ...

    # === Test Editing ===

    def add_runtime_comments(
        self, test_source: str, original_runtimes: dict[str, int], optimized_runtimes: dict[str, int]
    ) -> str:
        """Add runtime performance comments to test source code.

        Adds comments showing the original vs optimized runtime for each
        function call (e.g., "// 1.5ms -> 0.3ms (80% faster)").

        Args:
            test_source: Test source code to annotate.
            original_runtimes: Map of invocation IDs to original runtimes (ns).
            optimized_runtimes: Map of invocation IDs to optimized runtimes (ns).

        Returns:
            Test source code with runtime comments added.

        """
        ...

    def remove_test_functions(self, test_source: str, functions_to_remove: list[str]) -> str:
        """Remove specific test functions from test source code.

        Args:
            test_source: Test source code.
            functions_to_remove: List of function names to remove.

        Returns:
            Test source code with specified functions removed.

        """
        ...

    def postprocess_generated_tests(
        self, generated_tests: GeneratedTestsList, test_framework: str, project_root: Path, source_file_path: Path
    ) -> GeneratedTestsList:
        """Apply language-specific postprocessing to generated tests.

        Args:
            generated_tests: Generated tests to update.
            test_framework: Test framework used for the project.
            project_root: Project root directory.
            source_file_path: Path to the source file under optimization.

        Returns:
            Updated generated tests.

        """
        ...

    def remove_test_functions_from_generated_tests(
        self, generated_tests: GeneratedTestsList, functions_to_remove: list[str]
    ) -> GeneratedTestsList:
        """Remove specific test functions from generated tests.

        Args:
            generated_tests: Generated tests to update.
            functions_to_remove: List of function names to remove.

        Returns:
            Updated generated tests.

        """
        ...

    def add_runtime_comments_to_generated_tests(
        self,
        generated_tests: GeneratedTestsList,
        original_runtimes: dict[InvocationId, list[int]],
        optimized_runtimes: dict[InvocationId, list[int]],
        tests_project_rootdir: Path | None = None,
    ) -> GeneratedTestsList:
        """Add runtime comments to generated tests.

        Args:
            generated_tests: Generated tests to update.
            original_runtimes: Mapping of invocation IDs to original runtimes.
            optimized_runtimes: Mapping of invocation IDs to optimized runtimes.
            tests_project_rootdir: Root directory for tests (if applicable).

        Returns:
            Updated generated tests.

        """
        ...

    def add_global_declarations(self, optimized_code: str, original_source: str, module_abspath: Path) -> str:
        """Add new global declarations from optimized code to original source.

        Args:
            optimized_code: The optimized code that may contain new declarations.
            original_source: The original source code.
            module_abspath: Path to the module file (for parser selection).

        Returns:
            Original source with new declarations added.

        """
        ...

    def extract_calling_function_source(self, source_code: str, function_name: str, ref_line: int) -> str | None:
        """Extract the source code of a calling function.

        Args:
            source_code: Full source code of the file.
            function_name: Name of the function to extract.
            ref_line: Line number where the reference is.

        Returns:
            Source code of the function, or None if not found.

        """
        ...

    # === Test Result Comparison ===

    def compare_test_results(
        self,
        original_results_path: Path,
        candidate_results_path: Path,
        project_root: Path | None = None,
        project_classpath: str | None = None,
    ) -> tuple[bool, list]:
        """Compare test results between original and candidate code.

        Args:
            original_results_path: Path to original test results (e.g., SQLite DB).
            candidate_results_path: Path to candidate test results.
            project_root: Project root directory (for finding node_modules, etc.).
            project_classpath: Full project classpath string (Java only). Needed so
                the Comparator JVM can resolve project-specific classes during Kryo
                deserialization.

        Returns:
            Tuple of (are_equivalent, list of TestDiff objects).

        """
        ...

    @property
    def function_optimizer_class(self) -> type:
        """Return the FunctionOptimizer subclass for this language."""
        from codeflash.languages.function_optimizer import FunctionOptimizer

        return FunctionOptimizer

    def prepare_module(
        self, module_code: str, module_path: Path, project_root: Path
    ) -> tuple[dict[Path, ValidCode], ast.Module | None] | None:
        """Parse/validate a module before optimization."""
        ...

    def setup_test_config(self, test_cfg: TestConfig, file_path: Path, current_worktree: Path | None = None) -> bool:
        """One-time project setup after language detection. Default: no-op.

        Returns True if the project is valid for optimization, False otherwise.
        """
        return True

    def adjust_test_config_for_discovery(self, test_cfg: TestConfig) -> None:
        """Adjust test config before test discovery. Default: no-op."""

    def detect_module_system(self, project_root: Path, source_file: Path) -> str | None:
        """Detect the module system used by the project. Default: None (not applicable)."""
        return None

    def process_generated_test_strings(
        self,
        generated_test_source: str,
        instrumented_behavior_test_source: str,
        instrumented_perf_test_source: str,
        function_to_optimize: FunctionToOptimize,
        test_path: Path,
        test_cfg: Any,
        project_module_system: str | None,
    ) -> tuple[str, str, str]:
        """Process raw generated test strings (instrumentation, placeholder replacement, etc.).

        Returns (generated_test_source, instrumented_behavior_source, instrumented_perf_source).
        """
        ...

    # === Configuration ===

    def get_test_file_suffix(self) -> str:
        """Get the test file suffix for this language.

        Returns:
            Test file suffix (e.g., ".test.js", "_test.py").

        """
        ...

    def get_test_dir_for_source(self, test_dir: Path, source_file: Path | None) -> Path | None:
        """Find the appropriate test directory for a source file.

        For monorepos (JS), this finds the package's test directory from the source file path.
        Default implementation returns None (no special directory resolution needed).

        Args:
            test_dir: The root tests directory.
            source_file: Path to the source file being tested.

        Returns:
            The test directory path, or None if no special handling is needed.

        """
        return None

    def resolve_test_file_from_class_path(self, test_class_path: str, base_dir: Path) -> Path | None:
        """Resolve a test file path from a class path string.

        Languages with non-Python module systems (e.g., Java package names like
        "com.example.TestClass") override this to provide custom resolution.
        Default: returns None (fall through to shared Python/file-path logic).

        Args:
            test_class_path: The class path string from JUnit XML (e.g., "com.example.TestClass").
            base_dir: The base directory for tests.

        Returns:
            Path to the test file if found, None to fall through to default logic.

        """
        return None

    def resolve_test_module_path_for_pr(
        self, test_module_path: str, tests_project_rootdir: Path, non_generated_tests: set[Path]
    ) -> Path | None:
        """Resolve test module path to an absolute file path for PR creation.

        Languages with non-Python module naming (e.g., Java class names)
        override this. Default: returns None (fall through to shared logic).

        Args:
            test_module_path: The test module path string.
            tests_project_rootdir: The tests project root directory.
            non_generated_tests: Set of known non-generated test file paths.

        Returns:
            Resolved absolute path, or None to fall through to default logic.

        """
        return None

    def find_test_root(self, project_root: Path) -> Path | None:
        """Find the test root directory for a project.

        Args:
            project_root: Root directory of the project.

        Returns:
            Path to test root, or None if not found.

        """
        ...

    def get_runtime_files(self) -> list[Path]:
        """Get paths to runtime files that need to be copied to user's project.

        Returns:
            List of paths to runtime files (e.g., codeflash-jest-helper.js).

        """
        ...

    def ensure_runtime_environment(self, project_root: Path) -> bool:
        """Ensure the runtime environment is set up for the project.

        This method handles language-specific runtime setup, such as installing
        npm packages for JavaScript or pip packages for Python.

        Args:
            project_root: The project root directory.

        Returns:
            True if runtime environment is ready, False otherwise.

        """
        # Default implementation: just copy runtime files
        return False

    def create_dependency_resolver(self, project_root: Path) -> DependencyResolver | None:
        """Create a language-specific dependency resolver, if available.

        Returns:
            A DependencyResolver instance, or None if not supported.

        """
        return None

    def instrument_existing_test(
        self,
        test_path: Path,
        call_positions: Sequence[Any],
        function_to_optimize: Any,
        tests_project_root: Path,
        mode: str,
    ) -> tuple[bool, str | None]:
        """Inject profiling code into an existing test file.

        Wraps function calls with capture/benchmark instrumentation for
        behavioral verification and performance benchmarking.

        Args:
            test_path: Path to the test file.
            call_positions: List of code positions where the function is called.
            function_to_optimize: The function being optimized.
            tests_project_root: Root directory of tests.
            mode: Testing mode - "behavior" or "performance".

        Returns:
            Tuple of (success, instrumented_code).

        """
        ...

    def instrument_source_for_line_profiler(
        self, func_info: FunctionToOptimize, line_profiler_output_file: Path, project_classpath: str | None = None
    ) -> bool:
        """Instrument source code before line profiling."""
        ...

    def parse_line_profile_results(self, line_profiler_output_file: Path) -> dict[str, Any]:
        """Parse line profiler output."""
        ...

    # === Test Execution ===

    def generate_concolic_tests(
        self,
        test_cfg: TestConfig,
        project_root: Path,
        function_to_optimize: FunctionToOptimize,
        function_to_optimize_ast: Any,
    ) -> tuple[dict[str, Any], str]:
        """Generate concolic tests for a function.

        Default implementation returns empty results. Override for languages
        that support concolic testing (e.g. Python via CrossHair).
        """
        return {}, ""

    def run_line_profile_tests(
        self,
        test_paths: Any,
        test_env: dict[str, str],
        cwd: Path,
        timeout: int | None = None,
        project_root: Path | None = None,
        line_profile_output_file: Path | None = None,
    ) -> tuple[Path, Any]:
        """Run tests for line profiling.

        Args:
            test_paths: TestFiles object containing test file information.
            test_env: Environment variables for the test run.
            cwd: Working directory for running tests.
            timeout: Optional timeout in seconds.
            project_root: Project root directory.
            line_profile_output_file: Path where line profile results will be written.

        Returns:
            Tuple of (result_file_path, subprocess_result).

        """
        ...

    def run_behavioral_tests(
        self,
        test_paths: Any,
        test_env: dict[str, str],
        cwd: Path,
        timeout: int | None = None,
        project_root: Path | None = None,
        enable_coverage: bool = False,
        candidate_index: int = 0,
    ) -> tuple[Path, Any, Path | None, Path | None]:
        """Run behavioral tests for this language.

        Args:
            test_paths: TestFiles object containing test file information.
            test_env: Environment variables for the test run.
            cwd: Working directory for running tests.
            timeout: Optional timeout in seconds.
            project_root: Project root directory.
            enable_coverage: Whether to collect coverage information.
            candidate_index: Index of the candidate being tested.

        Returns:
            Tuple of (result_file_path, subprocess_result, coverage_path, config_path).

        """
        ...

    def run_benchmarking_tests(
        self,
        test_paths: Any,
        test_env: dict[str, str],
        cwd: Path,
        timeout: int | None = None,
        project_root: Path | None = None,
        min_loops: int = 5,
        max_loops: int = 100_000,
        target_duration_seconds: float = 10.0,
        inner_iterations: int = 100,
    ) -> tuple[Path, Any]:
        """Run benchmarking tests for this language.

        Args:
            test_paths: TestFiles object containing test file information.
            test_env: Environment variables for the test run.
            cwd: Working directory for running tests.
            timeout: Optional timeout in seconds.
            project_root: Project root directory.
            min_loops: Minimum number of loops for benchmarking.
            max_loops: Maximum number of loops for benchmarking.
            target_duration_seconds: Target duration for benchmarking in seconds.
            inner_iterations: Number of inner loop iterations per test method (Java only).

        Returns:
            Tuple of (result_file_path, subprocess_result).

        """
        ...


def convert_parents_to_tuple(parents: list[Any] | tuple[Any, ...]) -> tuple[FunctionParent, ...]:
    """Convert a list of parent objects to a tuple of FunctionParent.

    Args:
        parents: List or tuple of parent objects with name and type attributes.

    Returns:
        Tuple of FunctionParent objects.

    """
    return tuple(FunctionParent(name=p.name, type=p.type) for p in parents)
