"""
General action base class with automation for hot loading
"""
from importlib.util import spec_from_file_location, module_from_spec
from jaseci.utils.utils import logger
from jaseci.actions.remote_actions import ACTIONS_SPEC_LOC
from jaseci.actions.remote_actions import serv_actions, mark_as_remote
import requests
import os
import sys
import inspect
import importlib

live_actions = {}


def jaseci_action(act_group=None, aliases=list(), allow_remote=False):
    """Decorator for Jaseci Action interface"""
    caller_globals = dict(inspect.getmembers(inspect.currentframe().f_back))[
        "f_globals"
    ]
    if allow_remote and "serv_actions" not in caller_globals:
        caller_globals["serv_actions"] = serv_actions

    def decorator_func(func):
        if allow_remote:
            mark_as_remote([func, act_group, aliases, caller_globals])
        return assimilate_action(func, act_group, aliases)

    return decorator_func


def assimilate_action(func, act_group=None, aliases=list()):
    """Helper for jaseci_action decorator"""
    act_group = [func.__module__.split(".")[-1]] if act_group is None else act_group
    live_actions[f"{'.'.join(act_group+[func.__name__])}"] = func
    for i in aliases:
        live_actions[f"{'.'.join(act_group+[i])}"] = func
    return func


def load_local_actions(file: str):
    """Load all jaseci actions from python file"""
    name = file.rstrip(".py")
    name = ".".join(name.split("/")[-2:])
    # Assumes parent folder of py file is a package for internal relative
    # imports, name is package.module and package path is added to sys path
    spec = spec_from_file_location(name, str(file))
    if spec is None:
        logger.error(f"Cannot hot load from action file {file}")
        return False
    else:
        module_dir = os.path.dirname(os.path.dirname(os.path.realpath(file)))
        if module_dir not in sys.path:
            sys.path.append(module_dir)
        spec.loader.exec_module(module_from_spec(spec))
        return True


def load_module_actions(mod):
    """Load all jaseci actions from python module"""
    if importlib.import_module(mod):
        return True
    return False


def load_preconfig_actions(hook):
    import json

    action_preload = hook.resolve_glob("ACTION_SETS", None)
    if action_preload:
        action_preload = json.loads(action_preload)
        for i in action_preload["local"]:
            load_local_actions(i)
        for i in action_preload["remote"]:
            load_remote_actions(i)
        for i in action_preload["module"]:
            load_module_actions(i)


def get_global_actions(hook):
    """
    Loads all global action hooks for use by Jac programs
    Attaches globals to mem_hook
    """
    from jaseci.attr.action import action
    import uuid

    global_action_list = []
    for i in live_actions.keys():
        if (
            i.startswith("std.")
            or i.startswith("file.")
            or i.startswith("net.")
            or i.startswith("rand.")
            or i.startswith("vector.")
            or i.startswith("request.")
            or i.startswith("date.")
            or i.startswith("jaseci.")
        ):
            global_action_list.append(
                action(
                    m_id=uuid.UUID(int=0).urn,
                    h=hook,
                    mode="public",
                    name=i,
                    value=i,
                    persist=False,
                ).jid
            )
    return global_action_list


def load_remote_actions(url):
    """Load all jaseci actions from live pod"""
    headers = {"content-type": "application/json"}
    try:
        spec = requests.get(url.rstrip("/") + ACTIONS_SPEC_LOC, headers=headers)
        spec = spec.json()
        for i in spec.keys():
            live_actions[i] = gen_remote_func_hook(url, i, spec[i])
        return True

    except Exception as e:
        logger.error(f"Cannot hot load remote actions at {url}: {e}")
        return False


def gen_remote_func_hook(url, act_name, param_names):
    """Generater for function calls for remote action calls"""

    def func(*args):
        params = {}
        for i in range(len(param_names)):
            if i < len(args):
                params[param_names[i]] = args[i]
            else:
                params[param_names[i]] = None
        act_url = f"{url.rstrip('/')}/{act_name.split('.')[-1]}"
        res = requests.post(
            act_url, headers={"content-type": "application/json"}, json=params
        )
        return res.json()

    return func
