
from __future__ import absolute_import
from __future__ import division

import logging

from collections import namedtuple

from decorator import decorator

from vdsm.common.threadlocal import vars

from . import exception
from . import logutils
from . import response


_log = logging.getLogger("api")


Context = namedtuple('Context', "flow_id, client_host, client_port")


def logged(on=""):
    @decorator
    def method(func, *args, **kwargs):
        log = logging.getLogger(on)
        ctx = context_string(args[0])
        log.info('START %s %s', logutils.call2str(func, args, kwargs), ctx)
        try:
            ret = func(*args, **kwargs)
        except Exception as exc:
            log.info("FINISH %s error=%s %s", func.__name__, exc, ctx)
            raise
        log.info('FINISH %s return=%s %s', func.__name__, ret, ctx)
        return ret
    return method


def context_string(api_object):
    items = []

    ctx = getattr(vars, "context", None)
    if not ctx:
        items.append(("from", "internal"))
    else:
        items.append(("from", "%s,%s" % (ctx.client_host, ctx.client_port)))
        if ctx.flow_id:
            items.append(("flow_id", ctx.flow_id))

    task = getattr(vars, "task", None)
    if task:
        items.append(("task_id", task.id))

    attributes = getattr(api_object, 'LOG_ATTRIBUTES', {})
    if attributes:
        items.extend([(name, getattr(api_object, attr, '') or '',)
                      for name, attr in attributes.items()])

    return ", ".join(k + "=" + v for k, v in items)


def guard(*guarding_functions):
    @decorator
    def method(func, *args, **kwargs):
        for f in guarding_functions:
            f(*args, **kwargs)
        return func(*args, **kwargs)
    return method


@decorator
def method(func, *args, **kwargs):
    _log.debug("START %s args=%s kwargs=%s", func.__name__, args, kwargs)
    try:
        ret = func(*args, **kwargs)
    except exception.VdsmException as e:
        if e.expected:
            _log.info("FINISH %s error=%s", func.__name__, e)
        else:
            _log.exception("FINISH %s error=%s", func.__name__, e)
        return e.response()
    except Exception as e:
        _log.exception("FINISH %s error=%s", func.__name__, e)
        e = exception.GeneralException(str(e))
        return e.response()
    _log.debug("FINISH %s response=%s", func.__name__, ret)

    if response.is_valid(ret):
        return ret
    if ret is None:
        return response.success()
    return response.success(**ret)
