from pamqp import specification as spec
from .types import DeliveredMessage


class AMQPError(Exception):
    reason = 'An unspecified AMQP error has occurred: %s'

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.reason % self.args)


# Backward compatibility
AMQPException = AMQPError


class AMQPConnectionError(AMQPError):
    reason = 'Connection can not be opened'


class IncompatibleProtocolError(AMQPConnectionError):
    reason = 'The protocol returned by the server is not supported'


class AuthenticationError(AMQPConnectionError):
    reason = (
        'Server and client could not negotiate use of the '
        'authentication mechanisms. Server supports only %r, '
        'but client supports only %r.'
    )


class ProbableAuthenticationError(AMQPConnectionError):
    reason = (
        'Client was disconnected at a connection stage indicating a '
        'probable authentication error: %s'
    )


class ConnectionClosed(AMQPConnectionError):
    reason = 'The AMQP connection was closed (%s) %s'


class ConnectionSyntaxError(ConnectionClosed):
    reason = ('The sender sent a frame that contained illegal values for '
              'one or more fields. This strongly implies a programming error '
              'in the sending peer: %r')


class ConnectionFrameError(ConnectionClosed):
    reason = ('The sender sent a malformed frame that the recipient could '
              'not decode. This strongly implies a programming error '
              'in the sending peer: %r')


class ConnectionCommandInvalid(ConnectionClosed):
    reason = ('The client sent an invalid sequence of frames, attempting to '
              'perform an operation that was considered invalid by the server.'
              ' This usually implies a programming error in the client: %r')


class ConnectionChannelError(ConnectionClosed):
    reason = ('The client attempted to work with a channel that had not been '
              'correctly opened. This most likely indicates a fault in the '
              'client layer: %r')


class ConnectionUnexpectedFrame(ConnectionClosed):
    reason = ("The peer sent a frame that was not expected, usually in the "
              "context of a content header and body. This strongly indicates "
              "a fault in the peer's content processing: %r")


class ConnectionResourceError(ConnectionClosed):
    reason = ("The server could not complete the method because it lacked "
              "sufficient resources. This may be due to the client creating "
              "too many of some type of entity: %r")


class ConnectionNotAllowed(ConnectionClosed):
    reason = ("The client tried to work with some entity in a manner that is "
              "prohibited by the server, due to security settings or by "
              "some other criteria: %r")


class ConnectionNotImplemented(ConnectionClosed):
    reason = ("The client tried to use functionality that is "
              "not implemented in the server: %r")


class ConnectionInternalError(ConnectionClosed):
    reason = (" The server could not complete the method because of an "
              "internal error. The server may require intervention by an "
              "operator in order to resume normal operations: %r")


class AMQPChannelError(AMQPError):
    reason = 'An unspecified AMQP channel error has occurred'


class ChannelClosed(AMQPChannelError):
    reason = 'The channel was closed (%s) %s'


class ChannelAccessRefused(ChannelClosed):
    reason = ('The client attempted to work with a server entity to '
              'which it has no access due to security settings: %r')


class ChannelNotFoundEntity(ChannelClosed):
    reason = ('The client attempted to work with a server '
              'entity that does not exist: %r')


class ChannelLockedResource(ChannelClosed):
    reason = ('The client attempted to work with a server entity to '
              'which it has no access because another client is working '
              'with it: %r')


class ChannelPreconditionFailed(ChannelClosed):
    reason = ('The client requested a method that was not allowed because '
              'some precondition failed: %r')


class DuplicateConsumerTag(ChannelClosed):
    reason = 'The consumer tag specified already exists for this channel: %s'


class ProtocolSyntaxError(AMQPError):
    reason = 'An unspecified protocol syntax error occurred'


class InvalidFrameError(ProtocolSyntaxError):
    reason = 'Invalid frame received: %r'


class MethodNotImplemented(AMQPError):
    pass


class DeliveryError(AMQPError):
    __slots__ = 'message', 'frame'

    reason = "Error when delivery message %r, frame %r"

    def __init__(self, message: DeliveredMessage, frame: spec.Frame, *args):
        self.message = message
        self.frame = frame

        super().__init__(self.message, self.frame)


class PublishError(DeliveryError):
    reason = "%r for routing key %r"

    def __init__(self, message: DeliveredMessage, frame: spec.Frame, *args):
        assert isinstance(message.delivery, spec.Basic.Return)

        self.message = message
        self.frame = frame

        super(DeliveryError, self).__init__(
            message.delivery.reply_text,
            message.delivery.routing_key,
            *args
        )
