from inspect import getframeinfo, stack
from pathlib import Path

from mower.utils.path import get_path


def caller_info():
    caller = getframeinfo(stack()[2].frame)
    relative_name = Path(caller.filename)
    try:
        relative_name = relative_name.relative_to(get_path("@install"))
    except ValueError:
        pass
    return f"{relative_name}:{caller.lineno}"


def solver_name():
    """日志前缀

    利用调用栈的信息找出打印日志的代码来自哪里。

    第一部分是类名：
    1. 如果通过Solver调用（存在solver_name类属性），返回这个Solver的名字。如果solver_name为None，使用qualname替代；
    2. 没有通过Solver调用，返回调用栈最内层的类。

    其余两部分是调用栈最内层的函数名的行号。
    """

    # 遍历调用栈，记录最内层的类，并寻找Solver类。找到Solver类后返回；没有Solver类则返回最内层的类。
    caller_class = None  # 记录最内层的类
    stack_info = stack()
    func_name = stack_info[2].function
    lineno = stack_info[2].lineno
    for st in stack_info[2:]:
        try:
            caller = st.frame.f_locals["self"].__class__  # 不是类的会触发异常
            qualname = caller.__qualname__
            if caller_class is None:  # 更新最内层的类
                caller_class = qualname
            name = caller.solver_name  # 不是Solver的类会触发异常

            return [name or qualname, func_name, lineno]
        except Exception:
            pass
    return [caller_class, func_name, lineno]
