require 'digest/sha2'

module ActiveMerchant # :nodoc:
  module Billing # :nodoc:
    class Be2billGateway < Gateway
      self.test_url = 'https://secure-test.be2bill.com/front/service/rest/process.php'
      self.live_url = 'https://secure-magenta1.be2bill.com/front/service/rest/process.php'

      self.display_name = 'Be2Bill'
      self.homepage_url = 'http://www.be2bill.com/'
      self.supported_countries = ['FR']
      self.supported_cardtypes = %i[visa master american_express]
      self.default_currency = 'EUR'
      self.money_format = :cents

      # These options are mandatory on be2bill (cf. tests) :
      #
      # options = { :order_id    => order.id,
      #             :customer_id => user.id,
      #             :description => "Some description",
      #             :referrer    => request.env['HTTP_REFERER'],
      #             :user_agent  => request.env['HTTP_USER_AGENT'],
      #             :ip          => request.remote_ip,
      #             :email       => user.email }

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

      def authorize(money, creditcard, options = {})
        post = {}
        add_invoice(post, options)
        add_creditcard(post, creditcard)
        add_customer_data(post, options)

        commit('authorization', money, post)
      end

      def purchase(money, creditcard, options = {})
        post = {}
        add_invoice(post, options)
        add_creditcard(post, creditcard)
        add_customer_data(post, options)

        commit('payment', money, post)
      end

      def capture(money, authorization, options = {})
        post = {}
        add_invoice(post, options)
        post[:TRANSACTIONID] = authorization

        commit('capture', money, post)
      end

      private

      def add_customer_data(post, options)
        post[:CLIENTREFERRER]  = options[:referrer]
        post[:CLIENTUSERAGENT] = options[:user_agent]
        post[:CLIENTIP]        = options[:ip]
        post[:CLIENTEMAIL]     = options[:email]
        post[:CLIENTIDENT]     = options[:customer_id]
      end

      def add_invoice(post, options)
        post[:ORDERID]     = options[:order_id]
        post[:DESCRIPTION] = options[:description]
      end

      def add_creditcard(post, creditcard)
        post[:CARDFULLNAME]     = creditcard ? creditcard.name : ''
        post[:CARDCODE]         = creditcard ? creditcard.number : ''
        post[:CARDVALIDITYDATE] = creditcard ? '%02d-%02s' % [creditcard.month, creditcard.year.to_s[-2..-1]] : ''
        post[:CARDCVV]          = creditcard ? creditcard.verification_value : ''
      end

      def parse(response)
        ActiveSupport::JSON.decode(response)
      end

      def commit(action, money, parameters)
        parameters[:IDENTIFIER] = @options[:login]
        parameters[:AMOUNT]     = amount(money)
        parameters[:VERSION]    = '2.0'

        url = (test? ? self.test_url : self.live_url)
        response = parse(ssl_post(url, post_data(action, parameters)))

        Response.new(
          successful?(response),
          message_from(response),
          response,
          authorization: response['TRANSACTIONID'],
          test: test?
        )
      end

      def successful?(response)
        %w(0000 0001).include?(response['EXECCODE'])
      end

      def message_from(response)
        if successful?(response)
          "Approved : #{response['MESSAGE']}"
        else
          "Declined (#{response['EXECCODE']} - #{response['MESSAGE']}"
        end
      end

      def post_data(action, parameters = {})
        {
          method: action,
          params: parameters.merge(HASH: signature(parameters, action))
        }.to_query
      end

      def signature(parameters, action)
        parameters[:OPERATIONTYPE] = action unless parameters[:OPERATIONTYPE]

        signature = @options[:password]
        parameters.sort.each do |key, value|
          signature += ("#{key.upcase}=#{value}" + @options[:password])
        end

        Digest::SHA256.hexdigest(signature)
      end
    end
  end
end
