# frozen_string_literal: true

require 'solidus_braintree/request_protection'

module SolidusBraintree
  class Source < ::Spree::PaymentSource
    include RequestProtection

    PAYPAL = "PayPalAccount"
    APPLE_PAY = "ApplePayCard"
    VENMO = "VenmoAccount"
    CREDIT_CARD = "CreditCard"

    enum paypal_funding_source: {
      applepay: 0, bancontact: 1, blik: 2, boleto: 3, card: 4, credit: 5, eps: 6, giropay: 7, ideal: 8,
      itau: 9, maxima: 10, mercadopago: 11, mybank: 12, oxxo: 13, p24: 14, paylater: 15, paypal: 16, payu: 17,
      sepa: 18, sofort: 19, trustly: 20, venmo: 21, verkkopankki: 22, wechatpay: 23, zimpler: 24
    }, _suffix: :funding

    belongs_to :user, class_name: ::Spree::UserClassHandle.new, optional: true
    belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
    has_many :payments, as: :source, class_name: "Spree::Payment", dependent: :destroy

    belongs_to :customer, class_name: "SolidusBraintree::Customer", optional: true

    validates :payment_type, inclusion: [PAYPAL, APPLE_PAY, VENMO, CREDIT_CARD]

    before_validation :clear_device_data_if_blank
    before_save :clear_paypal_funding_source, unless: :paypal?

    scope(:with_payment_profile, -> { joins(:customer) })
    scope(:credit_card, -> { where(payment_type: CREDIT_CARD) })

    delegate :bin, :last_4, :card_type, :expiration_month, :expiration_year, :email,
      :username, :source_description, to: :braintree_payment_method, allow_nil: true

    # Aliases to match Spree::CreditCard's interface
    alias_method :last_digits, :last_4
    alias_method :month, :expiration_month
    alias_method :year, :expiration_year
    alias_method :cc_type, :card_type

    # we are not currenctly supporting an "imported" flag
    def imported
      false
    end

    def actions
      %w[capture void credit]
    end

    def can_capture?(payment)
      payment.pending? || payment.checkout?
    end

    def can_void?(payment)
      return false unless payment.response_code

      transaction = protected_request do
        braintree_client.transaction.find(payment.response_code)
      end
      Gateway::VOIDABLE_STATUSES.include?(transaction.status)
    rescue ActiveMerchant::ConnectionError
      false
    end

    def can_credit?(payment)
      payment.completed? && payment.credit_allowed > 0
    end

    def friendly_payment_type
      I18n.t(payment_type.underscore, scope: "solidus_braintree.payment_type")
    end

    def apple_pay?
      payment_type == APPLE_PAY
    end

    def paypal?
      payment_type == PAYPAL
    end

    def venmo?
      payment_type == VENMO
    end

    def reusable?
      token.present?
    end

    def credit_card?
      payment_type == CREDIT_CARD
    end

    def display_number
      if paypal?
        email
      elsif venmo?
        username
      else
        "XXXX-XXXX-XXXX-#{last_digits.to_s.rjust(4, 'X')}"
      end
    end

    def display_paypal_funding_source
      I18n.t(paypal_funding_source,
        scope: 'solidus_braintree.paypal_funding_sources',
        default: paypal_funding_source)
    end

    def display_payment_type
      "#{I18n.t('solidus_braintree.payment_type.label')}: #{friendly_payment_type}"
    end

    private

    def braintree_payment_method
      return unless braintree_client

      @braintree_payment_method ||= protected_request do
        braintree_client.payment_method.find(token)
      end
    rescue ActiveMerchant::ConnectionError, ArgumentError => e
      Rails.logger.warn("#{e}: token unknown or missing for #{inspect}")
      nil
    end

    def braintree_client
      @braintree_client ||= payment_method.try(:braintree)
    end

    def clear_device_data_if_blank
      self.device_data = nil if device_data.blank?
    end

    def clear_paypal_funding_source
      self.paypal_funding_source = nil
    end
  end
end