require "forwardable"

module Honeybadger
  module Breadcrumbs
    class Collector
      include Enumerable
      extend Forwardable
      # The Collector manages breadcrumbs and provides an interface for accessing
      # and affecting breadcrumbs
      #
      # Most actions are delegated to the current buffer implementation. A
      # Buffer must implement all delegated methods to work with the Collector.

      # Flush all breadcrumbs, delegates to buffer
      def_delegator :@buffer, :clear!

      # Iterate over all Breadcrumbs and satify Enumerable, delegates to buffer
      # @yield [Object] sequentially gives breadcrumbs to the block
      def_delegator :@buffer, :each

      # Raw Array of Breadcrumbs, delegates to buffer
      # @return [Array] Raw set of breadcrumbs
      def_delegator :@buffer, :to_a

      # Last item added to the buffer
      # @return [Breadcrumb]
      def_delegator :@buffer, :previous

      def initialize(config, buffer = RingBuffer.new)
        @config = config
        @buffer = buffer
      end

      # Add Breadcrumb to stack
      #
      # @return [self] Filtered breadcrumbs
      def add!(breadcrumb)
        return unless @config[:"breadcrumbs.enabled"]
        @buffer.add!(breadcrumb)

        self
      end

      alias_method :<<, :add!

      # @api private
      # Removes the prevous breadcrumb from the buffer if the supplied
      # block returns a falsy value
      #
      def drop_previous_breadcrumb_if
        @buffer.drop if previous && block_given? && yield(previous)
      end

      # All active breadcrumbs you want to remove a breadcrumb from the trail,
      # then you can selectively ignore breadcrumbs while building a notice.
      #
      # @return [Array] Active breadcrumbs
      def trail
        select(&:active?)
      end

      def to_h
        {
          enabled: @config[:"breadcrumbs.enabled"],
          trail: trail.map(&:to_h)
        }
      end

      private

      # @api private
      # Since the collector is shared with the worker thread, there is a chance
      # it can be cleared before we have prepared the request. We provide the
      # ability to duplicate a collector which should also duplicate the buffer
      # instance, as that holds the breadcrumbs.
      def initialize_dup(source)
        @buffer = source.instance_variable_get(:@buffer).dup
        super
      end
    end
  end
end
