class InventoryUnit < ActiveRecord::Base
  belongs_to :variant
  belongs_to :order
  belongs_to :shipment
  belongs_to :return_authorization

  scope :retrieve_on_hand, lambda {|variant, quantity| {:conditions => ["state = 'on_hand' AND variant_id = ?", variant], :limit => quantity}}

  # state machine (see http://github.com/pluginaweek/state_machine/tree/master for details)
  state_machine :initial => 'on_hand' do
    event :fill_backorder do
      transition :to => 'sold', :from => 'backordered'
    end
    event :ship do
      transition :to => 'shipped', :if => :allow_ship? #, :from => 'sold'
    end
    # TODO: add backorder state and relevant transitions
  end

  # destroy the specified number of on hand inventory units
  def self.destroy_on_hand(variant, quantity)
    inventory = self.retrieve_on_hand(variant, quantity)
    inventory.each do |unit|
      unit.destroy
    end
  end

  # create the specified number of on hand inventory units
  def self.create_on_hand(variant, quantity)
    quantity.times do
      self.create(:variant => variant, :state => 'on_hand')
    end
  end

  # grab the appropriate units from inventory, mark as sold and associate with the order
  def self.sell_units(order)

    # we should not already have inventory associated with the order at this point but we should clear to be safe (#1394)
    order.inventory_units.destroy_all

    out_of_stock_items = []
    order.line_items.each do |line_item|
      variant = line_item.variant
      quantity = line_item.quantity

      out_of_stock_items.concat create_units(order, variant, quantity)
    end
    out_of_stock_items
  end

  def self.adjust_units(order)
    units_by_variant = order.inventory_units.group_by(&:variant_id)
    out_of_stock_items = []

    #check line items quantities match
    order.line_items.each do |line_item|
      variant = line_item.variant
      quantity = line_item.quantity
      unit_count = units_by_variant.key?(variant.id) ? units_by_variant[variant.id].size : 0

      if unit_count < quantity
        out_of_stock_items.concat create_units(order, variant, (quantity - unit_count))
      elsif  unit_count > quantity
        (unit_count - quantity).times do
          inventory_unit = units_by_variant[variant.id].pop
          inventory_unit.restock!
        end
      end

      #remove it from hash as it's up-to-date
      units_by_variant.delete(variant.id)
    end

    #check for deleted line items (if theres anything left in units_by_variant its' extra)
    units_by_variant.each do |variant_id, units|
      units.each {|unit| unit.restock!}
    end

    out_of_stock_items
  end

  def can_restock?
    %w(sold shipped backordered).include?(state)
  end

  def restock!
    variant.update_attribute(:count_on_hand, variant.count_on_hand + 1) if Spree::Config[:track_inventory_levels] && !backordered?
    delete
  end

  # find the specified quantity of units with the specified status
  def self.find_by_status(variant, quantity, status)
    variant.inventory_units.find(:all,
                                 :conditions => ['status = ? ', status],
                                 :limit => quantity)
  end

  private
  def allow_ship?
    Spree::Config[:allow_backorder_shipping] || (state == 'ready_to_ship')
  end

  def self.create_units(order, variant, quantity)
    out_of_stock_items = []

    if Spree::Config[:track_inventory_levels]
      # mark all of these units as sold and associate them with this order
      remaining_quantity = variant.count_on_hand - quantity
      if (remaining_quantity >= 0)
        quantity.times do
          order.inventory_units.create(:variant => variant, :state => "sold")
        end
        variant.update_attribute(:count_on_hand, remaining_quantity)
      else
        (quantity + remaining_quantity).times do
          order.inventory_units.create(:variant => variant, :state => "sold")
        end
        if Spree::Config[:allow_backorders]
          (-remaining_quantity).times do
            order.inventory_units.create(:variant => variant, :state => "backordered")
          end
        else
          line_item.update_attribute(:quantity, quantity + remaining_quantity)
          out_of_stock_items << {:line_item => line_item, :count => -remaining_quantity}
        end
        variant.update_attribute(:count_on_hand, 0)
      end
    else
      # not tracking stock levels, so just create all inventory_units as sold (for shipping purposes)
      quantity.times do
        order.inventory_units.create(:variant => variant, :state => "sold")
      end
    end
    out_of_stock_items
  end

end
