class InventoryFetcher

  def initialize(arg)
    @inventory = arg[:inventory]
    @orders = arg[:orders]

    unless @inventory && @orders
      raise 'Konieczne jest zainicjalizowanie z argumentami :inventory i :orders.' 
    end
  end

  def add_to_cart(order_id, *items)
    item_selectors = []
    items.each do |item|
      item[:quantity].times do
        item_selectors << {:sku => item[:sku]}
      end
    end

    transition_state(order_id, item_selectors, {:from => AVAILABLE, :to => IN_CART})
  end
 
  def transition_state(order_id, selectors, opts={})
    items_transitioned = []
    begin # Użycie bloku begin-end, aby można było zapewnić obsługę błędów.

      for selector in selectors do
        query = selector.merge({:state => opts[:from]})
        physical_item = @inventory.find_and_modify({
            :query => query,
            :update => {
              '$set' => {
                :state => opts[:to],          # Stan docelowy.
                :ts => Time.now.utc           # Pobranie bieżącej daty i godziny klienta.
              }
            }
          })

        if physical_item.nil?
          raise InventoryFetchFailure
        end

        items_transitioned << physical_item['_id']   # Umieszczenie elementu w tablicy.
        @orders.update({:_id => order_id}, {
            '$push' => {
              :item_ids => physical_item['_id']
            }
          })
      end # Koniec pętli.

    rescue Mongo::OperationFailure, InventoryFetchFailure
      rollback(order_id, items_transitioned, opts[:from], opts[:to])
      raise InventoryFetchFailure, "Nie udało się dodać #{selector[:sku]}"
    end

    return items_transitioned.size
  end

  def rollback(order_id, item_ids, old_state, new_state)
    @orders.update({"_id" => order_id},
      {"$pullAll" => {:item_ids => item_ids}})

    item_ids.each do |id|
      @inventory.find_and_modify({
        :query => {
          "_id" => id,
          :state => new_state
        }
      },
      {
        :update => {
          "$set" => {
            :state => old_state, 
            :ts => Time.now.utc
          }
        }
      })
    end
  end
 
end
