# frozen_string_literal: false

# Responsible for keeping line items on initialised orders for a subscription in sync with
# the subscription line items on that subscription.

class LineItemSyncer
  def initialize(subscription, order_update_issues)
    @subscription = subscription
    @order_update_issues = order_update_issues
  end

  def sync!(order)
    update_item_quantities(order)
    create_new_items(order)
    destroy_obsolete_items(order)
  end

  private

  delegate :subscription_line_items, to: :subscription

  attr_reader :subscription, :order_update_issues

  def update_item_quantities(order)
    changed_subscription_line_items.each do |sli|
      line_item = order.line_items.find_by(variant_id: sli.variant_id)

      if line_item.blank?
        order_update_issues.add(order, sli.variant.product_and_full_name)
        next
      end

      unless update_quantity(line_item, sli)
        add_order_update_issue(order, line_item)
      end
    end
  end

  def create_new_items(order)
    new_subscription_line_items.each do |sli|
      new_line_item = order.line_items.create(variant_id: sli.variant_id,
                                              quantity: sli.quantity,
                                              skip_stock_check: skip_stock_check?(order))
      next if skip_stock_check?(order) || new_line_item.sufficient_stock?

      order.line_items.delete(new_line_item)
      add_order_update_issue(order, new_line_item)
    end
  end

  def destroy_obsolete_items(order)
    order.line_items.
      where(variant_id: subscription_line_items.
        select(&:marked_for_destruction?).
        map(&:variant_id)).
      destroy_all
  end

  def changed_subscription_line_items
    subscription_line_items.select{ |sli| sli.changed? && sli.persisted? }
  end

  def new_subscription_line_items
    subscription_line_items.select(&:new_record?)
  end

  def update_quantity(line_item, sli)
    if line_item.quantity == sli.quantity_was
      return line_item.update(quantity: sli.quantity,
                              skip_stock_check: skip_stock_check?(line_item.order))
    end
    line_item.quantity == sli.quantity
  end

  def skip_stock_check?(order)
    !order.complete?
  end

  def add_order_update_issue(order, line_item)
    issue_description = "#{line_item.product.name} - #{line_item.full_variant_name}"
    issue_description << " - #{stock_issue_description(line_item)}" if line_item.insufficient_stock?
    order_update_issues.add(order, issue_description)
  end

  def stock_issue_description(line_item)
    if line_item.variant.in_stock?
      I18n.t("admin.subscriptions.stock.insufficient_stock")
    else
      I18n.t("admin.subscriptions.stock.out_of_stock")
    end
  end
end
