module Groupify
  module ActiveRecord

    class NamedGroupCollection < Set
      def initialize(member)
        @member = member
        @named_group_memberships = member.group_memberships_as_member.named
        @group_names = @named_group_memberships.pluck(:group_name).map(&:to_sym)
        super(@group_names)
      end

      def add(named_group, opts={})
        named_group = named_group.to_sym
        membership_type = opts[:as]

        if @member.new_record?
          @member.group_memberships_as_member.build(group_name: named_group, membership_type: nil)
        else
          @member.transaction do
            @member.group_memberships_as_member.where(group_name: named_group, membership_type: nil).first_or_create!
          end
        end

        if membership_type
          if @member.new_record?
            @member.group_memberships_as_member.build(group_name: named_group, membership_type: membership_type)
          else
            @member.group_memberships_as_member.where(group_name: named_group, membership_type: membership_type).first_or_create!
          end
        end

        super(named_group)
      end

      alias_method :push, :add
      alias_method :<<, :add

      def merge(*args)
        opts = args.extract_options!
        named_groups = args.flatten
        named_groups.each do |named_group|
          add(named_group, opts)
        end
      end

      alias_method :concat, :merge

      def include?(named_group, opts={})
        named_group = named_group.to_sym
        if opts[:as]
          as(opts[:as]).include?(named_group)
        else
          super(named_group)
        end
      end

      def delete(*args)
        opts = args.extract_options!
        named_groups = args.flatten.compact

        remove(named_groups, :delete_all, opts)
      end

      def destroy(*args)
        opts = args.extract_options!
        named_groups = args.flatten.compact

        remove(named_groups, :destroy_all, opts)
      end

      def clear
        @named_group_memberships.delete_all
        super
      end

      alias_method :delete_all, :clear
      alias_method :destroy_all, :clear

      # Criteria to filter by membership type
      def as(membership_type)
        @named_group_memberships.as(membership_type).pluck(:group_name).map(&:to_sym)
      end

      protected

      def remove(named_groups, method, opts)
        if named_groups.present?
          scope = @named_group_memberships.where(group_name: named_groups)

          if opts[:as]
            scope = scope.where(membership_type: opts[:as])
          end

          scope.send(method)

          unless opts[:as]
            named_groups.each do |named_group|
              @hash.delete(named_group)
            end
          end
        end
      end
    end
  end
end
