# coding=utf-8
from __future__ import unicode_literals, print_function, absolute_import

import sys
import six
from .context import Request
from .context import Context
from .instance import api_manager
from .exceptions import GaiaRPCFaultException

try:
    from gm_logging.general import log_ratelimit
except ImportError:
    def log_ratelimit(*args, **kwargs):
        pass


class NestedInvoker(object):
    def __init__(self, ctx=None):
        assert isinstance(ctx, Context) or ctx is None
        self.__ctx = ctx

    def __invoke(self, method, params):
        try:
            ctx = self.__ctx
            if ctx:
                current_request = ctx._request
            else:
                current_request = None

            if current_request:
                assert isinstance(current_request, Request)
                current_method = current_request.method
                session_key = current_request.session_key
                environment = current_request.environment
            else:
                current_method = None
                session_key = None
                environment = {}

            # log_ratelimit({
            #     'position': 'gaia:NestedInvoker:__invoke',
            #     'old_method': current_method,
            #     'new_method': method,
            # })

            request = Request(
                method=method,
                params=params,
                session_key=session_key,
                environment=environment,
                is_nested_call=True,
            )
            result = api_manager.dispatch(request=request)
            if result['error'] != 0:
                raise GaiaRPCFaultException(
                    error=result['error'],
                    message=result['message'],
                    data=result['data'],
                )
            return Ok(result['data'])
        except Exception:
            return Err()

    def __getitem__(self, method):
        return lambda **params: self.__invoke(method, params)


class NestedInvokerForTest(NestedInvoker):
    def with_config(self, **kwargs):
        kwargs.pop('session_key', None)
        kwargs.pop('client_info', None)
        if kwargs:
            raise Exception('unexpected config: {}'.format(repr(kwargs)))
        return self


class Result(object):
    def unwrap(self):
        raise NotImplementedError

    def unwrap_or(self, value):
        raise NotImplementedError


class Ok(Result):
    def __init__(self, value):
        self.__value = value

    def unwrap(self):
        return self.__value

    def unwrap_or(self, value):
        return self.__value


class Err(Result):
    def __init__(self, exc_info=None):
        if exc_info is None:
            exc_info = sys.exc_info()
        self.__exc_info = exc_info

    def unwrap(self):
        six.reraise(*self.__exc_info)

    def unwrap_or(self, value):
        return value
