from abc import ABCMeta, abstractmethod
from collections import namedtuple

from six import add_metaclass


class Repository(
    namedtuple('Repository', ['id', 'name', 'namespace_name', 'description', 'is_public',
                              'kind'])):
  """
  Repository represents a namespaced collection of tags.
  :type id: int
  :type name: string
  :type namespace_name: string
  :type description: string
  :type is_public: bool
  :type kind: string
  """


class DerivedImage(namedtuple('DerivedImage', ['ref', 'blob', 'internal_source_image_db_id'])):
  """
  DerivedImage represents a user-facing alias for an image which was derived from another image.
  """


class RepositoryReference(namedtuple('RepositoryReference', ['id', 'name', 'namespace_name'])):
  """
  RepositoryReference represents a reference to a Repository, without its full metadata.
  """


class ImageWithBlob(
    namedtuple('Image', [
      'image_id', 'blob', 'compat_metadata', 'repository', 'internal_db_id', 'v1_metadata'])):
  """
  ImageWithBlob represents a user-facing alias for referencing an image, along with its blob.
  """


class Blob(namedtuple('Blob', ['uuid', 'size', 'uncompressed_size', 'uploading', 'locations'])):
  """
  Blob represents an opaque binary blob saved to the storage system.
  """


class TorrentInfo(namedtuple('TorrentInfo', ['piece_length', 'pieces'])):
  """
  TorrentInfo represents the torrent piece information associated with a blob.
  """


@add_metaclass(ABCMeta)
class VerbsDataInterface(object):
  """
  Interface that represents all data store interactions required by the registry's custom HTTP
  verbs.
  """

  @abstractmethod
  def get_repository(self, namespace_name, repo_name):
    """
    Returns a repository tuple for the repository with the given name under the given namespace.
    Returns None if no such repository was found.
    """
    pass

  @abstractmethod
  def get_manifest_layers_with_blobs(self, repo_image):
    """
    Returns the full set of manifest layers and their associated blobs starting at the given
    repository image and working upwards to the root image.
    """
    pass

  @abstractmethod
  def get_blob_path(self, blob):
    """
    Returns the storage path for the given blob.
    """
    pass

  @abstractmethod
  def get_derived_image_signature(self, derived_image, signer_name):
    """
    Returns the signature associated with the derived image and a specific signer or None if none.
    """
    pass

  @abstractmethod
  def set_derived_image_signature(self, derived_image, signer_name, signature):
    """
    Sets the calculated signature for the given derived image and signer to that specified.
    """
    pass

  @abstractmethod
  def delete_derived_image(self, derived_image):
    """
    Deletes a derived image and all of its storage.
    """
    pass

  @abstractmethod
  def set_blob_size(self, blob, size):
    """
    Sets the size field on a blob to the value specified.
    """
    pass

  @abstractmethod
  def get_repo_blob_by_digest(self, namespace_name, repo_name, digest):
    """
    Returns the blob with the given digest under the matching repository or None if none.
    """
    pass

  @abstractmethod
  def get_torrent_info(self, blob):
    """
    Returns the torrent information associated with the given blob or None if none.
    """
    pass

  @abstractmethod
  def set_torrent_info(self, blob, piece_length, pieces):
    """
    Sets the torrent infomation associated with the given blob to that specified.
    """
    pass

  @abstractmethod
  def lookup_derived_image(self, repo_image, verb, varying_metadata=None):
    """
    Looks up the derived image for the given repository image, verb and optional varying metadata
    and returns it or None if none.
    """
    pass

  @abstractmethod
  def lookup_or_create_derived_image(self, repo_image, verb, location, varying_metadata=None):
    """
    Looks up the derived image for the given repository image, verb and optional varying metadata
    and returns it. If none exists, a new derived image is created.
    """
    pass

  @abstractmethod
  def get_tag_image(self, namespace_name, repo_name, tag_name):
    """
    Returns the image associated with the live tag with the given name under the matching repository
    or None if none.
    """
    pass

  @abstractmethod
  def is_namespace_enabled(self, namespace_name):
    """ Returns whether the given namespace exists and is enabled. """
    pass