module Runcible
  module Extensions
    class Consumer < Runcible::Resources::Consumer
      # Bind a consumer to all repositories with a given ID
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               repo_id  the repo ID to bind to
      # @param  [String]               type_id the distributor type_id to bind to
      # @param  [Hash]                 options  options to pass to the bindings
      # @option  options [Boolean]     :notify_agent sends consumer a notification
      # @option  options [Hash]        :binding_config sends consumer a notification
      # @return [RestClient::Response]          set of tasks representing each bind operation
      def bind_all(id, repo_id, type_id, options = {})
        details = repository_extension.retrieve_with_details(repo_id)['distributors'].map do |d|
          bind(id, repo_id, d['id'], options) if d['distributor_type_id'] == type_id
        end
        details.compact.flatten
      end

      # Unbind a consumer to all repositories with a given ID
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               repo_id  the repo ID to unbind from
      # @param  [String]               type_id  the distributor type_id to unbind from
      # @return [RestClient::Response]          set of tasks representing each unbind operation
      def unbind_all(id, repo_id, type_id)
        details = repository_extension.retrieve_with_details(repo_id)['distributors'].map do |d|
          unbind(id, repo_id, d['id']) if d['distributor_type_id'] == type_id
        end
        details.compact.flatten
      end

      # Activate a consumer as a pulp node
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               update_strategy update_strategy for the node (defaults to additive)
      # @return [RestClient::Response]          response from update call
      def activate_node(id, update_strategy = 'additive')
        delta = {:notes => {'_child-node' => true,
                            '_node-update-strategy' => update_strategy}}
        self.update(id, delta)
      end

      # Deactivate a consumer as a pulp node
      #
      # @param  [String]               id       the consumer ID
      # @return [RestClient::Response]          response from update call
      def deactivate_node(id)
        delta = {:notes => {'child-node' => nil,
                            'update_strategy' => nil}}
        self.update(id, :delta => delta)
      end

      # Install content to a consumer
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               type_id  the type of content to install (e.g. rpm, errata)
      # @param  [Array]                units    array of units to install
      # @param  [Hash]                 options to pass to content install
      # @return [RestClient::Response]          task representing the install operation
      def install_content(id, type_id, units, options = {})
        install_units(id, generate_content(type_id, units), options)
      end

      # Update content on a consumer
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               type_id  the type of content to update (e.g. rpm, errata)
      # @param  [Array]                units    array of units to update
      # @param  [Hash]                 options to pass to content update
      # @return [RestClient::Response]          task representing the update operation
      def update_content(id, type_id, units, options = {})
        update_units(id, generate_content(type_id, units, options), options)
      end

      # Uninstall content from a consumer
      #
      # @param  [String]               id       the consumer ID
      # @param  [String]               type_id  the type of content to uninstall (e.g. rpm, errata)
      # @param  [Array]                units    array of units to uninstall
      # @return [RestClient::Response]          task representing the uninstall operation
      def uninstall_content(id, type_id, units)
        uninstall_units(id, generate_content(type_id, units))
      end

      # Generate the content units used by other functions
      #
      # @param  [String]  type_id the type of content (e.g. rpm, errata)
      # @param  [Array]   units   array of units
      # @param  [Hash]    options contains options which may impact the format of the content (e.g :all => true)
      # @return [Array]           array of formatted content units
      def generate_content(type_id, units, options = {})
        content = []

        case type_id
        when 'rpm', 'package_group'
          unit_key = :name
        when 'erratum'
          unit_key = :id
        when 'repository'
          unit_key = :repo_id
        else
          unit_key = :id
        end

        if options[:all]
          content_unit = {}
          content_unit[:type_id] = type_id
          content_unit[:unit_key] = {}
          content.push(content_unit)
        elsif units.nil?
          content = [{:unit_key => nil, :type_id => type_id}]
        else
          units.each do |unit|
            content_unit = {}
            content_unit[:type_id] = type_id
            content_unit[:unit_key] = if unit.is_a?(Hash)
                                        #allow user to pass in entire unit
                                        unit
                                      else
                                        { unit_key => unit }
                                      end

            content.push(content_unit)
          end
        end
        content
      end

      # Regenerate the applicability for a set of consumers
      #
      # @param [String, Array]         ids  array of consumer ids
      # @return [RestClient::Response]
      def regenerate_applicability_by_ids(ids)
        criteria = {
          'consumer_criteria' => { 'filters' => { 'id' => { '$in' => ids } } }
        }
        regenerate_applicability(criteria)
      end

      # Retrieve the set of errata that is applicable to a consumer(s)
      #
      # @param  [String, Array]         ids             string containing a single consumer id or an array of ids
      # @return [RestClient::Response]  content applicability hash with details of errata available to consumer(s)
      def applicable_errata(ids)
        applicable_for_type(ids, Runcible::Extensions::Errata.content_type)
      end

      # Retrieve the set of rpms that are applicable to a consumer(s)
      #
      # @param  [String, Array]         ids             string containing a single consumer id or an array of ids
      # @return [RestClient::Response]  content applicability hash with details of rpms available to consumer(s)
      def applicable_rpms(ids)
        applicable_for_type(ids, Runcible::Extensions::Rpm.content_type)
      end

      # Retrieve the set of modules that are applicable to a consumer(s)
      #
      # @param  [String, Array]         ids             string containing a single consumer id or an array of ids
      # @return [RestClient::Response]  content applicability hash with details of modules available to consumer(s)
      def applicable_module_streams(ids)
        applicable_for_type(ids, Runcible::Extensions::Module.content_type)
      end

      private

      def applicable_for_type(ids, type)
        ids = [ids] if ids.is_a? String

        criteria = {
          'criteria' => { 'filters' => { 'id' => { '$in' => ids } } },
          'content_types' => [type]
        }
        applicability(criteria)
      end

      def repository_extension
        Runcible::Extensions::Repository.new(self.config)
      end
    end
  end
end
