module Bunto
  module Filters
    module GroupingFilters
      # Group an array of items by a property
      #
      # input - the inputted Enumerable
      # property - the property
      #
      # Returns an array of Hashes, each looking something like this:
      #  {"name"  => "larry"
      #   "items" => [...] } # all the items where `property` == "larry"
      def group_by(input, property)
        if groupable?(input)
          groups = input.group_by { |item| item_property(item, property).to_s }
          grouped_array(groups)
        else
          input
        end
      end

      # Group an array of items by an expression
      #
      # input - the object array
      # variable - the variable to assign each item to in the expression
      # expression -a Liquid comparison expression passed in as a string
      #
      # Returns the filtered array of objects
      def group_by_exp(input, variable, expression)
        return input unless groupable?(input)

        parsed_expr = parse_expression(expression)
        @context.stack do
          groups = input.group_by do |item|
            @context[variable] = item
            parsed_expr.render(@context)
          end
          grouped_array(groups)
        end
      end

      private
      def parse_expression(str)
        Liquid::Variable.new(str, {})
      end

      private
      def groupable?(element)
        element.respond_to?(:group_by)
      end

      private
      def grouped_array(groups)
        groups.each_with_object([]) do |item, array|
          array << {
            "name"  => item.first,
            "items" => item.last,
            "size"  => item.last.size,
          }
        end
      end
    end
  end
end
