# The link between two {Actor actors}
#
# Each {Contact} has many {Tie ties}, which determine the kind of the link through {Relation relations}
#
# = {Contact Contacts} and {Activity activities}
# Each {Activity} is attached to a {Contact}. When _Alice_ post in _Bob_'s wall,
# the {Activity} is attached to the {Contact} from _Alice_ to _Bob_
#
# * The sender of the {Contact} is the author of the {Activity}.
#   It is the user that uploads a resource to the website or the social entity that
#   originates the {Activity} (for example: add as contact).
#
# * The receiver {Actor} of the {Contact} is the receiver of the {Activity}.
#   The {Activity} will appear in the wall of the receiver, depending on the permissions
#
class Contact < ActiveRecord::Base
  # Send a message when this contact is created or updated
  attr_accessor :message

  belongs_to :inverse,
             :class_name => "Contact"

  belongs_to :sender,
             :class_name => "Actor",
             :include => SocialStream.subjects
  belongs_to :receiver,
             :class_name => "Actor",
             :include => SocialStream.subjects

  has_many :ties
  has_many :relations, :through => :ties

  scope :sent_by, lambda { |a|
    where(:sender_id => Actor.normalize_id(a))
  }

  scope :received_by, lambda { |a|
    where(:receiver_id => Actor.normalize_id(a))
  }

  scope :sent_or_received_by, lambda { |a|
    where(arel_table[:sender_id].eq(Actor.normalize_id(a)).
          or(arel_table[:receiver_id].eq(Actor.normalize_id(a))))
  }

  scope :recent, order("contacts.created_at DESC")

  scope :pending, joins("LEFT JOIN contacts AS inverse_contacts ON inverse_contacts.id = contacts.inverse_id").
                  where(arel_table[:inverse_id].eq(nil).or(arel_table.alias("inverse_contacts")[:ties_count].eq(0)))

  scope :active, where(arel_table[:ties_count].gt(0))

  validates_presence_of :sender_id, :receiver_id
  validates_uniqueness_of :sender_id, :scope => :receiver_id
  validates_uniqueness_of :receiver_id, :scope => :sender_id

  after_create :set_inverse
  after_create :send_message

  def sender_subject
    sender.try(:subject)
  end

  def receiver_subject
    receiver.try(:subject)
  end

  # Does this {Contact} have the same sender and receiver?
  def reflexive?
    sender_id == receiver_id
  end

  # Find or create the inverse {Contact}
  def inverse!
    inverse ||
      receiver.contact_to!(sender)
  end

  # Is not the inverse of this {Contact}
  def pending?
    inverse &&
      inverse.ties_count > 0
  end

  # The {ActivityVerb} corresponding to this {Contact}, depending on if
  # it is pending or already replied
  def verb
    pending? ? "follow" : "make-friend"
  end

  # has_many collection=objects method does not trigger destroy callbacks,
  # so follower_count will not be updated
  #
  # We need to update that status here
  def relation_ids=(ids)
    remove_follower(ids)

    association(:relations).ids_writer(ids)
  end

  private

  def remove_follower(ids)
    # There was no follower
    return if relation_ids.blank?

    ids = ids.map(&:to_i)

    return if ids.sort == relation_ids.sort

    following = Relation.
                  where(:id => relation_ids).
                  joins(:permissions).
                  merge(Permission.follow).
                  any?

    if following
      will_follow = Relation.
                      where(:id => ids).
                      joins(:permissions).
                      merge(Permission.follow).
                      any?

      if !will_follow
        receiver.decrement!(:follower_count)
      end
    end
  end

  # Send a message to the contact receiver
  def send_message
    if message.present?
      sender.send_message(receiver, message, I18n.t("activity.verb.#{ verb }.#{ receiver.subject_type }.message", :name => sender.name))
    end
  end

  def set_inverse
    inverse = Contact.sent_by(receiver_id).received_by(sender_id).first

    return if inverse.blank?

    update_attribute :inverse_id, inverse.id
    inverse.update_attribute :inverse_id, id
  end
end