from abc import ABCMeta, abstractmethod
from collections import namedtuple

from six import add_metaclass

from endpoints.api import format_date


class Permission(namedtuple('Permission', ['repository_name', 'repository_visibility_name', 'role_name'])):
  """
  Permission the relationship between a robot and a repository and whether that robot can see the repo.
  """

  def to_dict(self):
    return {
      'repository': {
        'name': self.repository_name,
        'is_public': self.repository_visibility_name == 'public'
      },
      'role': self.role_name
    }


class Team(namedtuple('Team', ['name', 'avatar'])):
  """
  Team represents a team entry for a robot list entry.
  :type name: string
  :type avatar: {string -> string}
  """
  def to_dict(self):
    return {
      'name': self.name,
      'avatar': self.avatar,
    }


class RobotWithPermissions(
  namedtuple('RobotWithPermissions', [
    'name',
    'password',
    'created',
    'last_accessed',
    'teams',
    'repository_names',
    'description',
  ])):
  """
  RobotWithPermissions is a list of robot entries.
  :type name: string
  :type password: string
  :type created: datetime|None
  :type last_accessed: datetime|None
  :type teams: [Team]
  :type repository_names: [string]
  :type description: string
  """

  def to_dict(self, include_token=False):
    data = {
      'name': self.name,
      'created': format_date(self.created) if self.created is not None else None,
      'last_accessed': format_date(self.last_accessed) if self.last_accessed is not None else None,
      'teams': [team.to_dict() for team in self.teams],
      'repositories': self.repository_names,
      'description': self.description,
    }

    if include_token:
      data['token'] = self.password

    return data


class Robot(
  namedtuple('Robot', [
    'name',
    'password',
    'created',
    'last_accessed',
    'description',
    'unstructured_metadata',
  ])):
  """
  Robot represents a robot entity.
  :type name: string
  :type password: string
  :type created: datetime|None
  :type last_accessed: datetime|None
  :type description: string
  :type unstructured_metadata: dict
  """

  def to_dict(self, include_metadata=False, include_token=False):
    data = {
      'name': self.name,
      'created': format_date(self.created) if self.created is not None else None,
      'last_accessed': format_date(self.last_accessed) if self.last_accessed is not None else None,
      'description': self.description,
    }

    if include_token:
      data['token'] = self.password

    if include_metadata:
      data['unstructured_metadata'] = self.unstructured_metadata
    
    return data


@add_metaclass(ABCMeta)
class RobotInterface(object):
  """
  Interface that represents all data store interactions required by the Robot API
  """

  @abstractmethod
  def get_org_robot(self, robot_shortname, orgname):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def get_user_robot(self, robot_shortname, owning_user):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def create_user_robot(self, robot_shortname, owning_user):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def create_org_robot(self, robot_shortname, orgname):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def delete_robot(self, robot_username):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def regenerate_user_robot_token(self, robot_shortname, owning_user):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def regenerate_org_robot_token(self, robot_shortname, orgname):
    """

    Returns:
      Robot object

    """

  @abstractmethod
  def list_entity_robot_permission_teams(self, prefix, include_permissions=False,
                                         include_token=False, limit=None):
    """

    Returns:
      list of RobotWithPermissions objects

    """

  @abstractmethod
  def list_robot_permissions(self, username):
    """

    Returns:
      list of Robot objects

    """