class RecipeIngredient < ApplicationRecord

  belongs_to :food, optional: true
  belongs_to :recipe_as_ingredient, class_name: 'Recipe', optional: true
  belongs_to :recipe, inverse_of: :recipe_ingredients, touch: true

  validates :sort_order, presence: true

  def name
    if self.ingredient.present?
      self.ingredient.name
    else
      super
    end
  end

  def display_name
    str = [quantity, units, name].delete_if { |i| i.blank? }.join(' ')
    str << ", #{preparation}" if preparation.present?
    str
  end

  def ingredient_id
    case
      when recipe_as_ingredient_id
        "R#{recipe_as_ingredient_id}"
      when food_id
        "F#{food_id}"
      else
        nil
    end
  end

  def ingredient_id=(val)
    return val if self.ingredient_id == val

    @ingredient = nil
    case val
      when -> (v) { v.blank? }
        self.recipe_as_ingredient_id = nil
        self.food_id = nil
      when /^R(\d+)$/
        self.food_id = nil
        self.recipe_as_ingredient_id = $1.to_i
      when /^F(\d+)$/
        self.recipe_as_ingredient_id = nil
        self.food_id = $1.to_i
      else
        raise "Invalid ingredient_id: #{val}"
    end
  end

  def ingredient
    @ingredient ||= case
                         when self.recipe_as_ingredient_id
                           self.recipe_as_ingredient
                         when self.food_id
                           self.food
                         else
                          nil
                       end
  end

  def scale(factor, auto_unit = false)
    if factor.present? && self.quantity.present? && factor != '1'

      value_unit = UnitConversion.parse(self.quantity, self.units)
      value_unit = value_unit.scale(factor)

      if auto_unit
        value_unit = value_unit.auto_unit
      end

      self.quantity = value_unit.pretty_value
      self.units = value_unit.unit.to_s
    end
  end

  def to_metric
    return unless self.quantity.present?
    value_unit = UnitConversion.parse(self.quantity, self.units)
    value_unit = value_unit.to_metric

    self.quantity = value_unit.pretty_value
    self.units = value_unit.unit.to_s
  end

  def to_standard
    return unless self.quantity.present?
    value_unit = UnitConversion.parse(self.quantity, self.units)
    value_unit = value_unit.to_standard

    self.quantity = value_unit.pretty_value
    self.units = value_unit.unit.to_s
  end

  def to_volume
    return unless self.quantity.present?
    if ingredient && ingredient.density?
      density = UnitConversion.parse(ingredient.density)
      if density.density?
        value_unit = UnitConversion.parse(self.quantity, self.units)
        value_unit = value_unit.to_volume(density).auto_unit

        self.quantity = value_unit.pretty_value
        self.units = value_unit.unit.to_s
      end
    end
  end

  def to_mass
    return unless self.quantity.present?
    if ingredient && ingredient.density?
      UnitConversion::with_custom_units(self.ingredient.custom_units) do
        density = UnitConversion.parse(ingredient.density)
        if density.density?
          value_unit = UnitConversion.parse(self.quantity, self.units)
          value_unit = value_unit.to_mass(density).auto_unit

          self.quantity = value_unit.pretty_value
          self.units = value_unit.unit.to_s
        end
      end
    end
  end

  # Based on current quantity and units, return the value with with to multiply each nutrient to get the total amount
  # supplied by this ingredient
  def calculate_nutrition_ratio
    if self.ingredient.blank? || self.quantity.blank?
      return nil
    end

    UnitConversion::with_custom_units(self.ingredient.custom_units) do
      unit_is_each = self.units.blank? || %w(each ech item items per recipe recipes).include?(self.units.downcase)
      unit = UnitConversion::parse(self.quantity, unit_is_each ? 'each' : self.units)
      nutrition_unit = self.ingredient.nutrition_unit

      converted_unit = unit.convert(nutrition_unit.unit.unit, self.ingredient.density)
      converted_unit.scale(1.0 / nutrition_unit.value.value).raw_value
    end
  rescue UnitConversion::UnparseableUnitError
    nil
  end

  def log_copy
    copy = RecipeIngredient.new
    copy.food = self.food
    copy.recipe_as_ingredient = self.recipe_as_ingredient
    copy.name = self.name
    copy.sort_order = self.sort_order
    copy.quantity = self.quantity
    copy.units = self.units
    copy.preparation = self.preparation

    copy
  end

end
