require_relative 'browser_helpers'

module Killbill
  module PaypalExpress
    module HppSpecHelpers

      include ::Killbill::PaypalExpress::BrowserHelpers

      def hpp_setup
        @call_context = build_call_context
        @amount = BigDecimal.new('100')
        @currency = 'USD'
        @form_fields = @plugin.hash_to_properties(
            :order_id => '1234',
            :amount => @amount,
            :currency => @currency
        )
        kb_account_id = SecureRandom.uuid
        create_kb_account(kb_account_id, @plugin.kb_apis.proxied_services[:account_user_api])
        @pm = create_payment_method(::Killbill::PaypalExpress::PaypalExpressPaymentMethod, kb_account_id, @call_context.tenant_id)
        verify_payment_method kb_account_id
      end

      def validate_form(form)
        form.kb_account_id.should == @pm.kb_account_id
        form.form_url.should start_with('https://www.sandbox.paypal.com/cgi-bin/webscr')
        form.form_method.should == 'GET'
      end

      def validate_nil_form_property(form, key)
        key_properties = form.properties.select { |prop| prop.key == key }
        key_properties.size.should == 0
      end

      def validate_form_property(form, key, value=nil)
        key_properties = form.properties.select { |prop| prop.key == key }
        key_properties.size.should == 1
        key = key_properties.first.value
        value.nil? ? key.should_not(be_nil) : key.should == value
        key
      end

      def validate_token(form)
        login_and_confirm form.form_url
      end

      def purchase_and_refund(kb_payment_id, purchase_payment_external_key, purchase_properties)
        # Trigger the purchase
        payment_response = @plugin.purchase_payment(@pm.kb_account_id, kb_payment_id, purchase_payment_external_key, @pm.kb_payment_method_id, @amount, @currency, purchase_properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :PURCHASE
        payer_id = find_value_from_properties(payment_response.properties, 'payerId')
        payer_id.should_not be_nil

        # Verify GET API
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, [], @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :PURCHASE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id

        # Try a full refund
        refund_response = @plugin.refund_payment(@pm.kb_account_id, kb_payment_id, SecureRandom.uuid, @pm.kb_payment_method_id, @amount, @currency, [], @call_context)
        refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
        refund_response.amount.should == @amount
        refund_response.transaction_type.should == :REFUND

        # Verify GET API
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, [], @call_context)
        payment_infos.size.should == 2
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :PURCHASE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        payment_infos[1].kb_payment_id.should.should == kb_payment_id
        payment_infos[1].transaction_type.should == :REFUND
        payment_infos[1].amount.should == @amount
        payment_infos[1].currency.should == @currency
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil
      end

      def authorize_capture_and_refund(kb_payment_id, payment_external_key, properties, payment_processor_account_id)
        # Trigger the authorize
        payment_response = @plugin.authorize_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :AUTHORIZE
        payer_id = find_value_from_properties(payment_response.properties, 'payerId')
        payer_id.should_not be_nil

        # Verify GET AUTHORIZED PAYMENT
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id
        find_value_from_properties(payment_infos[0].properties, 'paymentInfoPaymentStatus').should == 'Pending'
        find_value_from_properties(payment_infos[0].properties, 'paymentInfoPendingReason').should == 'authorization'
        find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id

        # Trigger the capture
        payment_response = @plugin.capture_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :CAPTURE

        # Verify GET CAPTURED PAYMENT
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        # Two expected transactions: one auth and one capture
        payment_infos.size.should == 2
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id
        payment_infos[1].kb_payment_id.should == kb_payment_id
        payment_infos[1].transaction_type.should == :CAPTURE
        payment_infos[1].amount.should == @amount
        payment_infos[1].currency.should == @currency
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[1].properties, 'paymentInfoPaymentStatus').should == 'Completed'
        find_value_from_properties(payment_infos[1].properties, 'payment_processor_account_id').should == payment_processor_account_id

        # Try a full refund
        refund_response = @plugin.refund_payment(@pm.kb_account_id, kb_payment_id, SecureRandom.uuid, @pm.kb_payment_method_id, @amount, @currency, [], @call_context)
        refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
        refund_response.amount.should == @amount
        refund_response.transaction_type.should == :REFUND

        # Verify GET API
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 3
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id
        payment_infos[1].kb_payment_id.should == kb_payment_id
        payment_infos[1].transaction_type.should == :CAPTURE
        payment_infos[1].amount.should == @amount
        payment_infos[1].currency.should == @currency
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[1].properties, 'payment_processor_account_id').should == payment_processor_account_id
        payment_infos[2].kb_payment_id.should.should == kb_payment_id
        payment_infos[2].transaction_type.should == :REFUND
        payment_infos[2].amount.should == @amount
        payment_infos[2].currency.should == @currency
        payment_infos[2].status.should == :PROCESSED
        payment_infos[2].gateway_error.should == 'Success'
        payment_infos[2].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[2].properties, 'payment_processor_account_id').should == payment_processor_account_id
      end

      def authorize_and_double_capture(kb_payment_id, payment_external_key, properties)
        # Trigger the authorize
        payment_response = @plugin.authorize_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :AUTHORIZE

        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil

        # Trigger the capture
        payment_response = @plugin.capture_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :CAPTURE

        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 2
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        payment_infos[1].kb_payment_id.should == kb_payment_id
        payment_infos[1].transaction_type.should == :CAPTURE
        payment_infos[1].amount.should == @amount
        payment_infos[1].currency.should == @currency
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil

        # Trigger a capture again with full amount
        payment_response = @plugin.capture_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:ERROR), payment_response.gateway_error
        payment_response.amount.should == nil
        payment_response.transaction_type.should == :CAPTURE

        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 3
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        payment_infos[1].kb_payment_id.should == kb_payment_id
        payment_infos[1].transaction_type.should == :CAPTURE
        payment_infos[1].amount.should == @amount
        payment_infos[1].currency.should == @currency
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil
        payment_infos[2].kb_payment_id.should.should == kb_payment_id
        payment_infos[2].transaction_type.should == :CAPTURE
        payment_infos[2].amount.should be_nil
        payment_infos[2].currency.should be_nil
        payment_infos[2].status.should == :ERROR
        payment_infos[2].gateway_error.should == 'Authorization has already been completed.'
        payment_infos[2].gateway_error_code.should be_nil
      end

      def authorize_and_void(kb_payment_id, payment_external_key, properties, payment_processor_account_id)
        # Trigger the authorize
        payment_response = @plugin.authorize_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.amount.should == @amount
        payment_response.transaction_type.should == :AUTHORIZE

        # Verify get_payment_info
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id

        # Trigger the void
        payment_response = @plugin.void_payment(@pm.kb_account_id, kb_payment_id, payment_external_key, @pm.kb_payment_method_id, properties, @call_context)
        payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
        payment_response.transaction_type.should == :VOID

        # Verify get_payment_info
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
        # Two expected transactions: one auth and one capture
        payment_infos.size.should == 2
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should == @amount
        payment_infos[0].currency.should == @currency
        payment_infos[0].status.should == :PROCESSED
        payment_infos[0].gateway_error.should == 'Success'
        payment_infos[0].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id
        payment_infos[1].kb_payment_id.should == kb_payment_id
        payment_infos[1].transaction_type.should == :VOID
        payment_infos[1].status.should == :PROCESSED
        payment_infos[1].gateway_error.should == 'Success'
        payment_infos[1].gateway_error_code.should be_nil
        find_value_from_properties(payment_infos[1].properties, 'payment_processor_account_id').should == payment_processor_account_id
      end

      def purchase_with_missing_token
        failed_purchase([], :CANCELED, 'Could not find the payer_id: the token is missing', 'RuntimeError')
      end

      def authorize_with_missing_token
        failed_authorize([], :CANCELED, 'Could not find the payer_id: the token is missing', 'RuntimeError')
      end

      def purchase_with_invalid_token(purchase_properties)
        failed_purchase(purchase_properties, :CANCELED, "Could not find the payer_id for token #{properties_to_hash(purchase_properties)[:token]}", 'RuntimeError')
      end

      def authorize_with_invalid_token(authorize_properties)
        failed_authorize(authorize_properties, :CANCELED, "Could not find the payer_id for token #{properties_to_hash(authorize_properties)[:token]}", 'RuntimeError')
      end

      def subsequent_purchase(purchase_properties)
        failed_purchase(purchase_properties, :ERROR, 'A successful transaction has already been completed for this token.')
      end

      def failed_authorize(authorize_properties, status, msg, gateway_error_code=nil)
        kb_payment_id = SecureRandom.uuid

        payment_response = @plugin.authorize_payment(@pm.kb_account_id, kb_payment_id, SecureRandom.uuid, @pm.kb_payment_method_id, @amount, @currency, authorize_properties, @call_context)
        payment_response.status.should eq(status), payment_response.gateway_error
        payment_response.amount.should be_nil
        payment_response.transaction_type.should == :AUTHORIZE

        # Verify GET API
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, [], @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :AUTHORIZE
        payment_infos[0].amount.should be_nil
        payment_infos[0].currency.should be_nil
        payment_infos[0].status.should == status
        payment_infos[0].gateway_error.should == msg
        payment_infos[0].gateway_error_code.should == gateway_error_code
      end

      def failed_purchase(purchase_properties, status, msg, gateway_error_code=nil)
        kb_payment_id = SecureRandom.uuid

        payment_response = @plugin.purchase_payment(@pm.kb_account_id, kb_payment_id, SecureRandom.uuid, @pm.kb_payment_method_id, @amount, @currency, purchase_properties, @call_context)
        payment_response.status.should eq(status), payment_response.gateway_error
        payment_response.amount.should be_nil
        payment_response.transaction_type.should == :PURCHASE

        # Verify GET API
        payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, [], @call_context)
        payment_infos.size.should == 1
        payment_infos[0].kb_payment_id.should == kb_payment_id
        payment_infos[0].transaction_type.should == :PURCHASE
        payment_infos[0].amount.should be_nil
        payment_infos[0].currency.should be_nil
        payment_infos[0].status.should == status
        payment_infos[0].gateway_error.should == msg
        payment_infos[0].gateway_error_code.should == gateway_error_code
      end

      private

      def verify_payment_method(kb_account_id = nil)
        # Verify our table directly
        kb_account_id = @pm.kb_account_id if kb_account_id.nil?
        payment_methods = ::Killbill::PaypalExpress::PaypalExpressPaymentMethod.from_kb_account_id(kb_account_id, @call_context.tenant_id)
        payment_methods.size.should == 1
        payment_method = payment_methods.first
        payment_method.should_not be_nil
        payment_method.paypal_express_payer_id.should be_nil
        payment_method.token.should be_nil
        payment_method.kb_payment_method_id.should == @pm.kb_payment_method_id
      end
    end
  end
end