module ActsAsShareable  #:nodoc:
  module Shareable
  module Validations
  extend ActiveSupport::Concern
    included do
      validates :shareable_id, presence: true
    end
  end

    # Extends the Module ClassMethods
    def self.included(base)
      base.extend ClassMethods
    end

    module ClassMethods

      # Specify the types that the shareable objects can be shared with
      def acts_as_shareable(options={})
        has_many :shares, :as => :shareable, :dependent => :destroy
        has_many :group_shares, -> {where(shared_to_type: 'Group')}, :as => :shareable, class_name: 'Share'
        has_many :user_shares, -> {where(shared_to_type: 'User')}, :as => :shareable, class_name: 'Share'
        has_many :shared_with_individual_users, through: :shares, source: :shared_to, source_type: 'User'
        has_many :shared_with_groups, through: :shares, source: :shared_to, source_type: 'Group'
        has_many :shared_with_group_users, through: :shared_with_groups, source: :users
        include ActsAsShareable::Shareable::InstanceMethods
        extend ActsAsShareable::Shareable::SingletonMethods
        accepts_nested_attributes_for :shares, allow_destroy: true
      end
    end

    # Add class methods here
    module SingletonMethods

      # Find shares by users
      def find_shares_by_user(user, *opts)
        joins("LEFT OUTER JOIN shares s ON s.shareable_id = #{self.table_name}.id")
        .where("s.user_id = ? AND s.shareable_type =?", user.id, self)
      end

      # Find by shared to specified type
      def find_by_shared_to(object, *opts)
        joins("LEFT OUTER JOIN shares s ON s.shareable_id = #{self.table_name}.id")
        .where("s.shareable_type =? and s.shared_to_type=? and s.shared_to_id = ?", self, object.class.base_class, object.id)
      end

      # Find shares by users and by specified type
      def find_by_shared_to_and_user(object, user, *opts)
        joins("LEFT OUTER JOIN shares s ON s.shareable_id = #{self.table_name}.id")
        .where("s.user_id = ? AND s.shareable_type =? and s.shared_to_type=? and s.shared_to_id = ?", user.id, self, object.class.base_class, object.id)
      end

      private

      def merge_options(options, opts)
        if opts && opts[0].is_a?(Hash) && opts[0].has_key?(:conditions)
          cond = opts[0].delete(:conditions)
          options[:conditions][0] << " " << cond.delete_at(0)
          options[:conditions] + cond
        end
        options.merge!(opts[0]) if opts && opts[0].is_a?(Hash)
        return options
      end

      end

    # Add instance methods here
    module InstanceMethods
       
      #  Create new shareble
      def share_to(object, by_user)
        unless shared_to?(object, by_user)
          s = Share.new(user_id: by_user.id, shared_to_type: object.class.to_s, shared_to_id: object.id)
          self.shares << s
          self.save!
        end
      end
      
      # Remove shareble object
      def remove_share_from(object, by_user)
        shareable = self
        to = object.class.base_class
        s = Share.where("shareable_type = ? and shareable_id = ? and shared_to_type = ? and shared_to_id = ? and user_id=?", shareable, id, to, object.id, by_user.id)
        if s
          s.destroy
          reload
        end
      end
      
      # Find shareble with specific info
      def shared_to?(object, by_user)
        shareable = self
        to = object.class.base_class
        s = Share.where("shareable_type = ? and shareable_id = ? and shared_to_type = ? and shared_to_id = ? and user_id=?", shareable, id, to, object.id, by_user.id)
        return !s.nil?
      end
      
      # Users to whom the object is shared
      def shared_with_users
        shared_with_individual_users.union(shared_with_group_users).uniq
      end

    end
  end

end
