import inspect
from importlib.metadata import PackageNotFoundError, version
from typing import Any, Optional


def get_package_version() -> str:
    """Get the package version of galileo."""
    try:
        return version("galileo")
    except PackageNotFoundError:
        return "0.0.0"  # Unknown version


def get_method_name() -> str:
    """Get the entry point method name into the galileo package.

    Returns the last function before exiting the galileo package when
    traversing from the bottom of the call stack. This captures any entry
    point including direct usage of autogenerated resources or utils.

    Returns
    -------
    str
        A string in format "{function_name}@{module_name}" or empty string if not found.
    """
    frame = inspect.currentframe()
    try:
        # Collect all frames
        frames_info: list[dict[str, Any]] = []
        temp_frame = frame
        while temp_frame is not None:
            module = inspect.getmodule(temp_frame)
            module_name: str = module.__name__ if module else "<unknown>"
            function_name: str = temp_frame.f_code.co_name
            filename: str = temp_frame.f_code.co_filename
            lineno: int = temp_frame.f_lineno

            frames_info.append({"module": module_name, "function": function_name, "file": filename, "line": lineno})
            temp_frame = temp_frame.f_back

        # Find the entry point into galileo package (traversing from bottom to top)
        entry_point: Optional[dict[str, Any]] = None
        for i in range(len(frames_info) - 1, -1, -1):
            frame_info = frames_info[i]
            module_name = str(frame_info["module"])  # Explicitly cast to string

            # Check if this frame is from the galileo package
            # Include ALL galileo.* modules (resources, utils, etc.)
            if module_name.startswith("galileo."):
                entry_point = frame_info
                break

        # Return the formatted method name
        if entry_point:
            return f"{entry_point['function']}@{entry_point['module']}"

        return ""
    finally:
        # Clean up frame references to avoid reference cycles
        del frame


def get_sdk_header() -> str:
    """Build the X-Galileo-SDK header value."""
    version = get_package_version()
    method_name = get_method_name()

    sdk_header = f"galileo-python/{version}"
    if method_name:
        sdk_header = f"{sdk_header} {method_name}"

    return sdk_header
