def flatten_error_dict(d, key=''):
    items = []
    for k, v in d.items():
        new_key = key + '.' + k if key else k

        if isinstance(v, dict):
            try:
                _errors = v['_errors']
            except KeyError:
                items.extend(flatten_error_dict(v, new_key).items())
            else:
                items.append((new_key, ' '.join(x.get('message', '') for x in _errors)))
        else:
            items.append((new_key, v))

    return dict(items)


class VKApiError(Exception):
    pass


class CommandError(Exception):
    pass


class CommandNotFound(Exception):
    pass


class DisabledCommand(CommandError):
    pass


class CommandOnCooldown(CommandError):
    pass


class CheckFailure(Exception):
    pass


class ClientException(Exception):
    pass


class CommandInvokeError(Exception):
    pass


class ArgumentError(Exception):
    pass


class BadArgument(ArgumentError):
    pass


class BadUnionArgument(BadArgument):
    pass


class MissingRequiredArgument(Exception):
    pass


class TooManyArguments(Exception):
    pass


class ConversionError(Exception):
    pass


class ExtensionError(Exception):
    def __init__(self, message=None, *args, name):
        self.name = name
        m = message or 'Extension {!r} had an error.'.format(name)
        super().__init__(m, *args)


class ExtensionAlreadyLoaded(ExtensionError):
    def __init__(self, name):
        super().__init__('Extension {!r} is already loaded.'.format(name), name=name)


class ExtensionNotLoaded(ExtensionError):
    def __init__(self, name):
        super().__init__('Extension {!r} has not been loaded.'.format(name), name=name)


class NoEntryPointError(ExtensionError):
    def __init__(self, name):
        super().__init__("Extension {!r} has no 'setup' function.".format(name), name=name)


class ExtensionFailed(ExtensionError):
    def __init__(self, name, original):
        self.original = original
        fmt = 'Extension {0!r} raised an error: {1.__class__.__name__}: {1}'
        super().__init__(fmt.format(name, original), name=name)


class ExtensionNotFound(ExtensionError):
    def __init__(self, name, original):
        self.original = original
        fmt = 'Extension {0!r} could not be loaded.'
        super().__init__(fmt.format(name), name=name)


class UnexpectedQuoteError(ArgumentError):
    def __init__(self, quote):
        self.quote = quote
        super().__init__('Unexpected quote mark, {0!r}, in non-quoted string'.format(quote))


class InvalidEndOfQuotedStringError(ArgumentError):
    def __init__(self, char):
        self.char = char
        super().__init__('Expected space after closing quotation but received {0!r}'.format(char))


class ExpectedClosingQuoteError(ArgumentError):
    def __init__(self, close_quote):
        self.close_quote = close_quote
        super().__init__('Expected closing {}.'.format(close_quote))


class HTTPException(Exception):

    def __init__(self, response, message):
        self.response = response
        self.status = response.status
        if isinstance(message, dict):
            self.code = message.get('code', 0)
            base = message.get('message', '')
            errors = message.get('errors')
            if errors:
                errors = flatten_error_dict(errors)
                helpful = '\n'.join('In %s: %s' % t for t in errors.items())
                self.text = base + '\n' + helpful
            else:
                self.text = base
        else:
            self.text = message
            self.code = 0

        fmt = '{0.status} {0.reason} (error code: {1})'
        if len(self.text):
            fmt = fmt + ': {2}'

        super().__init__(fmt.format(self.response, self.code, self.text))


class Forbidden(HTTPException):
    pass


class NotFound(HTTPException):
    pass


class LoginError(Exception):
    pass


class MissingPermissions(Exception):
    def __init__(self, permission):
        self.permission = permission
        super().__init__(f'Missing permission: {self.permission}')


class NotInUserList(Exception):
    pass
