# Blocca l'invio accidentale di numeri di carta completi all'API Stripe dal backend.
# I dati carta devono essere raccolti solo da Stripe Checkout / Elements lato browser.
module StripePciGuard
  RAW_CARD_KEYS = %w[number cvc exp_month exp_year].freeze

  def self.raw_card_hash?(value)
    return false unless value.is_a?(Hash)

    key_set = value.keys.map(&:to_s)
    (key_set & RAW_CARD_KEYS).any?
  end

  def self.guard_params!(params)
    return unless params.is_a?(Hash)

    type = params[:type] || params["type"]
    card = params[:card] || params["card"]
    payment_method_data = params[:payment_method_data] || params["payment_method_data"]

    if type.to_s == "card" && raw_card_hash?(card)
      raise_security_error!
    end

    pmd_card = payment_method_data.is_a?(Hash) ? (payment_method_data[:card] || payment_method_data["card"]) : nil
    raise_security_error! if raw_card_hash?(pmd_card)
  end

  def self.raise_security_error!
    raise SecurityError,
          "PCI: non inviare numeri di carta completi all'API Stripe dal server. " \
          "Usa Stripe Checkout (redirect) o Stripe Elements lato client."
  end
end

if defined?(Stripe::PaymentMethod)
  Stripe::PaymentMethod.singleton_class.prepend(
    Module.new do
      def create(params = {}, opts = {})
        StripePciGuard.guard_params!(params)
        super
      end
    end
  )
end

if defined?(Stripe::Token)
  Stripe::Token.singleton_class.prepend(
    Module.new do
      def create(params = {}, opts = {})
        StripePciGuard.guard_params!(params)
        super
      end
    end
  )
end
