import inspect
from factors.register import DynamicFactorRuntime
import numpy as np


def str2fun(func_str):
    func_name = func_str.split("def ")[1].split("(")[0].strip()
    namespace = {"np": np}
    try:
        exec(func_str, namespace)
        return namespace[func_name]
    except Exception as e:
        print(f"Error loading function {func_str}: {str(e)}")


def exec_expression_func(func_str, **args):
    # Create a local namespace to capture the exec'd function
    local_vars = {}
    exec(func_str, globals(), local_vars)

    # Get the function name (you may parse it, or know it beforehand)
    func = next(v for k, v in local_vars.items() if callable(v))

    return func(**args)


def safe_exec_factor(
    factor,
    prices=None,
    returns=None,
    volumes=None,
    open_=None,
    close=None,
    high=None,
    low=None,
):
    """
    Executes a factor function or callable class instance, passing only the parameters it expects.
    Supports parameters: prices, returns, volumes, open, close, high, low.
    """
    # Handle function vs. class with .func
    sig = (
        inspect.signature(factor.func)
        if hasattr(factor, "func")
        else inspect.signature(factor)
    )
    expected_args = list(sig.parameters.keys())

    kwargs = {}
    if "prices" in expected_args and prices is not None:
        kwargs["prices"] = prices
    if "returns" in expected_args and returns is not None:
        kwargs["returns"] = returns
    if "volume" in expected_args and volumes is not None:
        kwargs["volume"] = volumes
    if "volumes" in expected_args and volumes is not None:
        kwargs["volumes"] = volumes  # for both 'volume' and 'volumes'
    if "open" in expected_args and open_ is not None:
        kwargs["open"] = open_
    if "close" in expected_args and close is not None:
        kwargs["close"] = close
    if "high" in expected_args and high is not None:
        kwargs["high"] = high
    if "low" in expected_args and low is not None:
        kwargs["low"] = low

    try:
        score = factor(**kwargs)
        return score
    except Exception as e:
        fname = getattr(factor, "__name__", None)
        if fname is None and hasattr(factor, "func"):
            fname = getattr(factor.func, "__name__", str(factor))
        else:
            fname = str(factor)
        print(f"[Error] Evaluating factor {fname}: {e}")
        return np.nan


def factor_validation(factor, length=30):
    assert isinstance(factor, DynamicFactorRuntime)

    # Generate positive integer prices rounded to 100 (like stock prices)
    dummy_prices = np.round(np.abs(np.random.randn(length)) * 100 + 100, 2)
    dummy_prices = np.maximum(dummy_prices, 1)  # Ensure no zero or negative prices

    # Generate random returns centered around 0 (typical daily returns)
    dummy_returns = np.random.normal(0, 0.02, length)  # Mean 0, std dev 2%

    score = safe_exec_factor(factor, dummy_prices, dummy_returns)
    return not np.isnan(score)


if __name__ == "__main__":
    dummy_returns = np.random.rand(5, 5)

    def_code = """def test_run_avg(*args, **kwargs):
    a=np.array(prices)
    if a:
        print(a)
    return np.mean(kwargs['prices'])
"""
    import pdb

    pdb.set_trace()
    result = exec_expression_func(def_code, prices=dummy_returns)
    print("Result:", result)
