from .api_errors import *
from .models import UserToken
api_methods_dict = {}


def __make_invalid_argument_type_error(name, value, except_type):
    related = {"param_name": name, "excepted_type": "int", "value": value}
    raise Exception(API_ERROR_INVALID_ARGUMENT_TYPE, related)


def api_make_response(response):
    return API_OK_OBJ | {"response": response}


def api_get_param_int(params: dict, name: str, required=True, default=None):
    if name in params:
        try:
            return int(params[name])
        except:
            __make_invalid_argument_type_error(name, params[name], "int")

    if required:
        raise Exception(API_ERROR_MISSING_ARGUMENT, name)

    return default


def api_get_param_str(params: dict, name: str, required=True, default=""):
    if name in params:
        return params[name]

    if required:
        raise Exception(API_ERROR_MISSING_ARGUMENT, name)

    return None


def api_get_access_token(params: dict, unused_name, required=True):
    token = api_get_param_str(params, "access_token", required)[0]
    print(f"checking token '{token}'")
    return UserToken.get_by_token(token)


def api_make_param(name, arg_class, description, required=True):
    return {
        "name": name,
        "type": arg_class,
        "description": description,
        "required": required
    }


API_PARAM_ACCESS_TOKEN = api_make_param(
    "access_token",
    UserToken,
    "<i>Токен</i>, выданный методом <code>account.auth</code>"
)


def api_method(func_name, doc="", params: list or None = None, returns=""):
    """
    Декоратор для методов API, автоматически передает параметры методам
    """
    def actual_decorator(func):
        def wrapper(**kwargs):
            print(f"> call method {func_name} with params {kwargs}. method params: {params}")

            errors = []
            func_args = {}
            for p in params:
                parser_funcs = {
                    str: api_get_param_str,
                    int: api_get_param_int,
                    UserToken: api_get_access_token
                }

                try:
                    if p["type"] not in parser_funcs:
                        raise Exception(API_ERROR_INTERNAL_ERROR, f"param type {p['type']} is unsupported")
                    func_args[p["name"]] = parser_funcs[p["type"]](kwargs, p["name"], p["required"])
                except Exception as ex:
                    errors.append(ex)
            print(f"errors: {errors}, args: {func_args}")
            if len(errors) > 0:
                return make_error_object(errors)
            else:
                out = func(**func_args)
                if out is None:
                    raise Exception(API_ERROR_INTERNAL_ERROR, "method returned null object")
                return out

        api_methods_dict[func_name] = {
            "doc": doc,
            "params": params,
            "func": wrapper,
            "return": returns
        }
        return wrapper

    return actual_decorator

