module ActiveMerchant # :nodoc:
  module Billing # :nodoc:
    class MicropaymentGateway < Gateway
      self.display_name = 'micropayment'
      self.homepage_url = 'https://www.micropayment.de/'

      self.test_url = self.live_url = 'https://sipg.micropayment.de/public/creditcardpsp/v1/nvp/'

      self.supported_countries = %w(DE)
      self.default_currency = 'EUR'
      self.money_format = :cents
      self.supported_cardtypes = %i[visa master american_express]

      def initialize(options = {})
        requires!(options, :access_key)
        super
      end

      def purchase(amount, payment_method, options = {})
        post = {}
        add_invoice(post, amount, options)
        add_payment_method(post, payment_method, options)
        add_customer_data(post, options)
        add_address(post, options)
        commit('purchase', post)
      end

      def authorize(amount, payment_method, options = {})
        post = {}
        add_invoice(post, amount, options)
        add_payment_method(post, payment_method, options)
        add_customer_data(post, options)
        add_address(post, options)
        commit('authorize', post)
      end

      def capture(amount, authorization, options = {})
        post = {}
        add_reference(post, authorization)
        add_invoice(post, amount, options)
        commit('capture', post)
      end

      def void(authorization, options = {})
        post = {}
        add_reference(post, authorization)
        commit('void', post)
      end

      def refund(amount, authorization, options = {})
        post = {}
        add_reference(post, authorization)
        add_invoice(post, amount, options)
        commit('refund', post)
      end

      def verify(credit_card, options = {})
        MultiResponse.run(:use_first_response) do |r|
          r.process { authorize(250, credit_card, options) }
          r.process(:ignore_result) { void(r.authorization, options) }
        end
      end

      def supports_scrubbing?
        true
      end

      def scrub(transcript)
        transcript.
          gsub(%r((accessKey=)\w+), '\1[FILTERED]').
          gsub(%r((number=)\d+), '\1[FILTERED]').
          gsub(%r((cvc2=)\d+), '\1[FILTERED]')
      end

      private

      def add_invoice(post, money, options)
        if money
          post[:amount] = amount(money)
          post[:currency] = options[:currency] || currency(money)
        end
        post[:project] = options[:project] if options[:project]
        post['params[title]'] = options[:description] if options[:description]
      end

      def add_payment_method(post, payment_method, options = {})
        post[:number] = payment_method.number
        post[:recurring] = 1 if options[:recurring] == true
        post[:cvc2] = payment_method.verification_value
        post[:expiryYear] = format(payment_method.year, :four_digits)
        post[:expiryMonth] = format(payment_method.month, :two_digits)

        post['params[firstname]'] = payment_method.first_name
        post['params[surname]'] = payment_method.last_name
      end

      def add_customer_data(post, options)
        post['params[email]'] = options[:email] if options[:email]
        post['params[ip]'] = options[:ip] || '1.1.1.1'
        post['params[sendMail]'] = options[:send_mail] || 'false'
      end

      def add_address(post, options)
        address = options[:billing_address]
        return unless address

        post['params[address]'] = address[:address1] if address[:address1]
        post['params[zipcode]'] = address[:zip] if address[:zip]
        post['params[town]'] = address[:city] if address[:city]
        post['params[country]'] = address[:country] if address[:country]
      end

      def add_reference(post, authorization)
        session_id, transaction_id = split_authorization(authorization)
        post[:sessionId] = session_id
        post[:transactionId] = transaction_id
      end

      def commit(action, params)
        params[:testMode] = 1 if test?
        params[:accessKey] = @options[:access_key]
        params[:apiKey] = @options[:api_key] || 'af1fd841af792f4c50131414ff76e004'

        response = parse(ssl_post(url(action), post_data(action, params), headers))

        Response.new(
          succeeded = success_from(response),
          message_from(succeeded, response),
          response,
          authorization: authorization_from(response, params),
          avs_result: AVSResult.new(code: response['some_avs_result_key']),
          cvv_result: CVVResult.new(response['some_cvv_result_key']),
          test: test?
        )
      end

      def headers
        { 'Content-Type' => 'application/x-www-form-urlencoded;charset=UTF-8' }
      end

      def post_data(action, params)
        params.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join('&')
      end

      def url(action)
        action_url = test? ? test_url : live_url
        "#{action_url}?action=#{action}"
      end

      def parse(body)
        body.split(/\r?\n/).inject({}) do |acc, pair|
          key, value = pair.split('=')
          acc[key] = CGI.unescape(value)
          acc
        end
      end

      def success_from(response)
        response['error'] == '0' &&
          response['transactionResultCode'] == '00' &&
          response['transactionStatus'] == 'SUCCESS'
      end

      def message_from(succeeded, response)
        if succeeded
          'Succeeded'
        else
          response['errorMessage'] || response['transactionResultMessage']
        end
      end

      def split_authorization(authorization)
        authorization.split('|')
      end

      def authorization_from(response, request_params)
        session_id = response['sessionId'] || request_params[:sessionId]
        "#{session_id}|#{response['transactionId']}"
      end
    end
  end
end
