"""Resolve functions to their fully-qualified source via Jedi."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Any

import jedi  # type: ignore[import-untyped]

from .._model import FunctionSource
from ..analysis._reference_graph import (
    get_qualified_name as get_qualified_name,  # noqa: PLC0414
)

if TYPE_CHECKING:
    from pathlib import Path

    from .._model import FunctionToOptimize

log = logging.getLogger(__name__)


_jedi_project_cache: dict[str, Any] = {}


def get_jedi_project(project_root: str) -> Any:
    """Return a cached Jedi project for *project_root*."""
    try:
        return _jedi_project_cache[project_root]
    except KeyError:
        project = jedi.Project(path=project_root)
        _jedi_project_cache[project_root] = project
        return project


def get_function_source(
    function: FunctionToOptimize,
    project_root: Path,
) -> FunctionSource:
    """Resolve *function* to a *FunctionSource* via Jedi."""
    project = get_jedi_project(str(project_root))
    script = jedi.Script(
        path=function.file_path,
        project=project,
    )
    names = script.get_names(
        all_scopes=True,
        definitions=True,
        references=False,
    )

    for name in names:
        try:
            if (
                name.type == "function"
                and name.full_name
                and name.name == function.function_name
                and name.full_name.startswith(name.module_name)
                and get_qualified_name(
                    name.module_name,
                    name.full_name,
                )
                == function.qualified_name
            ):
                return FunctionSource(
                    file_path=function.file_path,
                    qualified_name=function.qualified_name,
                    fully_qualified_name=name.full_name,
                    source_code=name.get_line_code(),
                )
        except Exception:  # noqa: PERF203
            log.exception(
                "Error resolving %s",
                function.qualified_name,
            )
            continue

    msg = f"Could not find {function.function_name!r} in {function.file_path}"
    raise ValueError(msg)
