# SocialStream provides a sophisticated and powerful system of permissions based on the relations
# and ties of the social network.
#
# Permissions are composed by action, objective and function. Action and objective are classical
# in content management systems, e.g. "create" "activity", "update" "tie", "read" "post"
#
# function is a novel feature. It supports applying the permission to certain set of ties.
# This set of ties changes along with the formation of ties in your website.
#
# Permissions are assigned to relations, and through relations, to ties. 
# When a sender establishes a tie with a receiver, she is granting to the receiver the permissions assigned
# to relation of the tie she has just established. For example, when Alice establishes a "friend" tie
# to Bob, she is granting him the permissions associated with "friend" relation.
#
# One of this permissions can be "read" "activity" "star_set". This way, Bob will have access
# to read the activities attached to ties inside the "star_set", which are the ties from Alice to her "friends"
#
class Permission < ActiveRecord::Base
  has_many :relation_permissions, :dependent => :destroy
  has_many :relations, :through => :relation_permissions

  scope :represent, where(:action => 'represent')

  # The SQL and ARel conditions for permission queries
  ParameterConditions = {
    :table => {
      'tie' =>
        "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND ties_as.relation_id = ties.relation_id",
      'weak_set' =>
        "ties_as.sender_id = ties.sender_id AND ties_as.receiver_id = ties.receiver_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt",
      'star_set' =>
        "ties_as.sender_id = ties.sender_id AND ties_as.relation_id = ties.relation_id",
      'weak_star_set' =>
        "ties_as.sender_id = ties.sender_id AND relations.lft BETWEEN relations_as.lft AND relations_as.rgt"
    },
    :arel => {
      'tie' => lambda { |as, t|
        # The same sender, receiver and relation
        as[:sender_id].eq(t.sender_id).and(
          as[:receiver_id].eq(t.receiver_id)).and(
          as[:relation_id].eq(t.relation_id))
      },
      'weak_set' => lambda { |as, t|
        # The same sender and receiver, but a stronger or equal relation
        as[:sender_id].eq(t.sender_id).and(
          as[:receiver_id].eq(t.receiver_id)).and(
          as[:relation_id].in(t.relation.stronger_or_equal.map(&:id)))
      },
      'star_set' => lambda { |as, t|
        # The same receiver and relation
        as[:sender_id].eq(t.sender_id).and(
          as[:relation_id].eq(t.relation_id))
      },
      'weak_star_set' => lambda { |as, t|
        # The same receiver with stronger or equal relations
        as[:sender_id].eq(t.sender_id).and(
          as[:relation_id].in(t.relation.stronger_or_equal.map(&:id)))
      }
    }
  }

  class << self
    def parameter_conditions(tie = nil)
      if tie.present?
        ParameterConditions[:arel].inject([]) { |conditions, h|
          # Add the condition 'permissions.function = key'
          # to all arel ParameterConditions
          conditions <<
            h.last.call(Tie.arel_table, tie).and(arel_table[:function].eq(h.first))
        }.inject(nil){ |result, pc|
          # Join all ParameterConditions with OR
          result.nil? ? pc : result.or(pc)
        }
      else
        ParameterConditions[:table].inject([]){ |result, pc|
          result <<
            sanitize_sql([ "#{ pc.last } AND permissions.function = ?", pc.first ])
        }.join(" OR ")
      end
    end
  end
end