# frozen_string_literal: true

module OrderCompletion
  extend ActiveSupport::Concern

  def order_completion_reset(order)
    distributor = order.distributor
    token = order.token

    expire_current_order
    build_new_order(distributor, token)

    session[:access_token] = current_order.token
    flash[:notice] = t(:order_processed_successfully)
  end

  private

  # Clears the cached order. Required for #current_order to return a new order to serve as cart.
  # See https://github.com/spree/spree/blob/1-3-stable/core/lib/spree/core/controller_helpers/order.rb#L14
  def expire_current_order
    session[:order_id] = nil
    @current_order = nil
  end

  # Builds an order setting the token and distributor of the one specified
  def build_new_order(distributor, token)
    new_order = current_order(true)
    new_order.assign_distributor!(distributor)
    new_order.tokenized_permission.token = token
    new_order.tokenized_permission.save!
  end

  def load_checkout_order
    @order = current_order

    order_invalid! if order_invalid_for_checkout?
  end

  def order_completion_route
    main_app.order_path(@order, order_token: @order.token)
  end

  def order_failed_route(step: 'details')
    main_app.checkout_step_path(step:)
  end

  def order_invalid_for_checkout?
    !@order || @order.completed? || !@order.checkout_allowed?
  end

  def order_invalid!
    Alert.raise_with_record("Notice: invalid order loaded during checkout", @order)

    flash[:error] = t('checkout.order_not_loaded')
    redirect_to main_app.shop_path
  end

  def process_payment_completion!
    unless @order.process_payments!
      payment_failed
      return redirect_to order_failed_route(step: 'payment')
    end

    if Orders::WorkflowService.new(@order).next && @order.complete?
      processing_succeeded
      redirect_to order_completion_route
    else
      processing_failed
      redirect_to order_failed_route
    end
  rescue Spree::Core::GatewayError => e
    gateway_error(e)
    processing_failed
    redirect_to order_failed_route
  end

  def processing_succeeded
    Checkout::PostCheckoutActions.new(@order).success(params, spree_current_user)
    order_completion_reset(@order)
  end

  def payment_failed
    notify_failure
  end

  def processing_failed
    notify_failure
    Checkout::PostCheckoutActions.new(@order).failure
  end

  def notify_failure(error = RuntimeError.new(order_processing_error))
    Alert.raise_with_record(error, @order)
    flash[:error] = order_processing_error if flash.blank?
  end

  def order_processing_error
    return t(:payment_processing_failed) if @order.errors.blank?

    @order.errors.full_messages.to_sentence
  end

  def gateway_error(error)
    flash[:error] = t(:spree_gateway_error_flash_for_checkout, error: error.message)
  end
end
