module Spree
  class ReturnAuthorization < Spree::Base
    belongs_to :order, class_name: 'Spree::Order', inverse_of: :return_authorizations

    has_many :return_items, inverse_of: :return_authorization, dependent: :destroy
    has_many :inventory_units, through: :return_items
    has_many :customer_returns, through: :return_items

    belongs_to :stock_location
    belongs_to :reason, class_name: 'Spree::ReturnAuthorizationReason', foreign_key: :return_authorization_reason_id
    before_create :generate_number

    after_save :generate_expedited_exchange_reimbursements

    accepts_nested_attributes_for :return_items, allow_destroy: true

    validates :order, presence: true
    validates :reason, presence: true
    validates :stock_location, presence: true
    validate :must_have_shipped_units, on: :create


    # These are called prior to generating expedited exchanges shipments.
    # Should respond to a "call" method that takes the list of return items
    class_attribute :pre_expedited_exchange_hooks
    self.pre_expedited_exchange_hooks = []

    state_machine initial: :authorized do
      before_transition to: :canceled, do: :cancel_return_items

      event :cancel do
        transition to: :canceled, from: :authorized
      end

    end

    extend DisplayMoney
    money_methods :pre_tax_total

    def pre_tax_total
      return_items.sum(:pre_tax_amount)
    end

    def currency
      order.nil? ? Spree::Config[:currency] : order.currency
    end

    def refundable_amount
      order.pre_tax_item_amount + order.promo_total
    end

    def customer_returned_items?
      customer_returns.exists?
    end

    private

      def must_have_shipped_units
        if order.nil? || order.inventory_units.shipped.none?
          errors.add(:order, Spree.t(:has_no_shipped_units))
        end
      end

      def generate_number
        self.number ||= loop do
          random = "RA#{Array.new(9){rand(9)}.join}"
          break random unless self.class.exists?(number: random)
        end
      end

      def cancel_return_items
        return_items.each(&:cancel!)
      end

      def generate_expedited_exchange_reimbursements
        return unless Spree::Config[:expedited_exchanges]

        items_to_exchange = return_items.select(&:exchange_required?)
        items_to_exchange.each(&:attempt_accept)
        items_to_exchange.select!(&:accepted?)

        return if items_to_exchange.blank?

        pre_expedited_exchange_hooks.each { |h| h.call items_to_exchange }

        reimbursement = Reimbursement.new(return_items: items_to_exchange, order: order)

        if reimbursement.save
          reimbursement.perform!
        else
          errors.add(:base, reimbursement.errors.full_messages)
          raise ActiveRecord::RecordInvalid.new(self)
        end

      end
  end
end