# frozen_string_literal: true

module Spree
  # Service object for creating new payments on an Order
  class PaymentCreate
    # @param order [Order] The order for the new payment
    # @param attributes [Hash,ActionController::Parameters] attributes which are assigned to the new payment
    #   * :payment_method_id Id of payment method used for this payment
    #   * :source_attributes Attributes used to build the source of this payment. Usually a {CreditCard}
    #     * :wallet_payment_source_id (Integer): The id of a {WalletPaymentSource} to use
    # @param request_env [Hash] rack env of user creating the payment
    # @param payment [Payment] Internal use only. Instead of making a new payment, change the attributes for an existing one.
    def initialize(order, attributes, payment: nil, request_env: {})
      @order = order
      @payment = payment

      # If AC::Params are passed in, attributes.to_h gives us a hash of only
      # the permitted attributes.
      @attributes = attributes.to_h.with_indifferent_access
      @source_attributes = @attributes.delete(:source_attributes) || {}
      @request_env = request_env
    end

    # Build the new Payment
    # @return [Payment] a new (unpersisted) Payment
    def build
      @payment ||= order.payments.new
      @payment.request_env = @request_env if @request_env
      @payment.attributes = @attributes

      if source_attributes[:wallet_payment_source_id].present?
        build_from_wallet_payment_source
      else
        build_source
      end

      @payment
    end

    private

    attr_reader :order, :payment, :attributes, :source_attributes

    def build_source
      payment_method = payment.payment_method
      if source_attributes.present? && payment_method.try(:payment_source_class)
        payment.source = payment_method.payment_source_class.new(source_attributes)
        payment.source.payment_method_id = payment_method.id
        if order && payment.source.respond_to?(:user=)
          payment.source.user = order.user
        end
      end
    end

    def build_from_wallet_payment_source
      wallet_payment_source_id = source_attributes.fetch(:wallet_payment_source_id)
      raise(ActiveRecord::RecordNotFound) if order.user.nil?
      wallet_payment_source = order.user.wallet.find(wallet_payment_source_id)
      raise(ActiveRecord::RecordNotFound) if wallet_payment_source.nil?
      build_from_payment_source(wallet_payment_source.payment_source)
    end

    def build_from_payment_source(payment_source)
      # FIXME: does this work?
      if source_attributes[:verification_value]
        payment_source.verification_value = source_attributes[:verification_value]
      end

      payment.source = payment_source
      payment.payment_method_id = payment_source.payment_method_id
    end

    def available_cards
      if user_id = order.user_id
        Spree::CreditCard.where(user_id:)
      else
        Spree::CreditCard.none
      end
    end
  end
end
