import bitmath


class V2RegistryException(Exception):
  def __init__(self, error_code_str, message, detail, http_status_code=400, repository=None,
               scopes=None):
    super(V2RegistryException, self).__init__(message)
    self.http_status_code = http_status_code
    self.repository = repository
    self.scopes = scopes

    self._error_code_str = error_code_str
    self._detail = detail

  def as_dict(self):
    return {
      'code': self._error_code_str,
      'message': self.message,
      'detail': self._detail if self._detail is not None else {},}


class BlobUnknown(V2RegistryException):
  def __init__(self, detail=None):
    super(BlobUnknown, self).__init__('BLOB_UNKNOWN', 'blob unknown to registry', detail, 404)


class BlobUploadInvalid(V2RegistryException):
  def __init__(self, detail=None):
    super(BlobUploadInvalid, self).__init__('BLOB_UPLOAD_INVALID', 'blob upload invalid', detail)


class BlobUploadUnknown(V2RegistryException):
  def __init__(self, detail=None):
    super(BlobUploadUnknown, self).__init__('BLOB_UPLOAD_UNKNOWN',
                                            'blob upload unknown to registry', detail, 404)


class DigestInvalid(V2RegistryException):
  def __init__(self, detail=None):
    super(DigestInvalid, self).__init__('DIGEST_INVALID',
                                        'provided digest did not match uploaded content', detail)


class ManifestBlobUnknown(V2RegistryException):
  def __init__(self, detail=None):
    super(ManifestBlobUnknown, self).__init__('MANIFEST_BLOB_UNKNOWN',
                                              'manifest blob unknown to registry', detail)


class ManifestInvalid(V2RegistryException):
  def __init__(self, detail=None, http_status_code=400):
    super(ManifestInvalid, self).__init__('MANIFEST_INVALID', 'manifest invalid', detail,
                                          http_status_code)


class ManifestUnknown(V2RegistryException):
  def __init__(self, detail=None):
    super(ManifestUnknown, self).__init__('MANIFEST_UNKNOWN', 'manifest unknown', detail, 404)

class TagExpired(V2RegistryException):
  def __init__(self, message=None, detail=None):
    super(TagExpired, self).__init__('TAG_EXPIRED',
                                     message or 'Tag has expired',
                                     detail,
                                     404)


class ManifestUnverified(V2RegistryException):
  def __init__(self, detail=None):
    super(ManifestUnverified, self).__init__('MANIFEST_UNVERIFIED',
                                             'manifest failed signature verification', detail)


class NameInvalid(V2RegistryException):
  def __init__(self, detail=None, message=None):
    super(NameInvalid, self).__init__('NAME_INVALID', message or 'invalid repository name', detail)


class NameUnknown(V2RegistryException):
  def __init__(self, detail=None):
    super(NameUnknown, self).__init__('NAME_UNKNOWN', 'repository name not known to registry',
                                      detail, 404)


class SizeInvalid(V2RegistryException):
  def __init__(self, detail=None):
    super(SizeInvalid, self).__init__('SIZE_INVALID',
                                      'provided length did not match content length', detail)


class TagAlreadyExists(V2RegistryException):
  def __init__(self, detail=None):
    super(TagAlreadyExists, self).__init__('TAG_ALREADY_EXISTS', 'tag was already pushed', detail,
                                           409)


class TagInvalid(V2RegistryException):
  def __init__(self, detail=None):
    super(TagInvalid, self).__init__('TAG_INVALID', 'manifest tag did not match URI', detail)


class LayerTooLarge(V2RegistryException):
  def __init__(self, uploaded=None, max_allowed=None):
    detail = {}
    message = 'Uploaded blob is larger than allowed by this registry'

    if uploaded is not None and max_allowed is not None:
      detail = {
        'reason': '%s is greater than maximum allowed size %s' % (uploaded, max_allowed),
        'max_allowed': max_allowed,
        'uploaded': uploaded,}

      up_str = bitmath.Byte(uploaded).best_prefix().format("{value:.2f} {unit}")
      max_str = bitmath.Byte(max_allowed).best_prefix().format("{value:.2f} {unit}")
      message = 'Uploaded blob of %s is larger than %s allowed by this registry' % (up_str,
                                                                                    max_str)


class Unauthorized(V2RegistryException):
  def __init__(self, detail=None, repository=None, scopes=None):
    super(Unauthorized,
          self).__init__('UNAUTHORIZED', 'access to the requested resource is not authorized',
                         detail, 401, repository=repository, scopes=scopes)


class Unsupported(V2RegistryException):
  def __init__(self, detail=None, message=None):
    super(Unsupported, self).__init__('UNSUPPORTED', message or 'The operation is unsupported.',
                                      detail, 405)


class InvalidLogin(V2RegistryException):
  def __init__(self, message=None):
    super(InvalidLogin, self).__init__('UNAUTHORIZED', message or
                                       'Specified credentials are invalid', {}, 401)


class InvalidRequest(V2RegistryException):
  def __init__(self, message=None):
    super(InvalidRequest, self).__init__('INVALID_REQUEST', message or 'Invalid request', {}, 400)


class NamespaceDisabled(V2RegistryException):
  def __init__(self, message=None):
    message = message or 'This namespace is disabled. Please contact your system administrator.'
    super(NamespaceDisabled, self).__init__('NAMESPACE_DISABLED', message, {}, 400)