module ActiveMerchant #:nodoc:
  module Billing #:nodoc:
    module SageCore #:nodoc:
      def self.included(base)
        base.cattr_accessor :source
        base.supported_countries = ['US', 'CA']
        base.homepage_url = 'http://www.sagepayments.com'
        base.display_name = 'Sage Payment Solutions'
      end

      # Transactions types:
      # <tt>01</tt> - Sale
      # <tt>02</tt> - AuthOnly
      # <tt>03</tt> - Force/PriorAuthSale
      # <tt>04</tt> - Void
      # <tt>06</tt> - Credit
      # <tt>11</tt> - PriorAuthSale by Reference*
      TRANSACTIONS = {
        :purchase           => '01',
        :authorization      => '02',
        :capture            => '11',
        :void               => '04',
        :credit             => '06'
      }

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

      private
      def add_invoice(post, options)
        post[:T_ordernum] = options[:order_id].slice(0, 20)
        post[:T_tax] = amount(options[:tax]) unless options[:tax].blank?
        post[:T_shipping] = amount(options[:shipping]) unless options[:shipping].blank?
      end

      def add_reference(post, reference)
        ref, source = reference.to_s.split(";")
        post[:T_reference] = ref
      end

      def add_amount(post, money)
        post[:T_amt] = amount(money)
      end

      def add_customer_data(post, options)
        post[:T_customer_number] = options[:customer] if Float(options[:customer]) rescue nil
      end

      def add_addresses(post, options)
        billing_address   = options[:billing_address] || options[:address] || {}

        post[:C_address]    = billing_address[:address1]
        post[:C_city]       = billing_address[:city]

        if ['US', 'CA'].include?(billing_address[:country])
          post[:C_state]    = billing_address[:state]
        else
          post[:C_state]    = "Outside of United States"
        end

        post[:C_zip]        = billing_address[:zip]
        post[:C_country]    = billing_address[:country]
        post[:C_telephone]  = billing_address[:phone]
        post[:C_fax]        = billing_address[:fax]
        post[:C_email]      = options[:email]

        if shipping_address = options[:shipping_address]
          post[:C_ship_name]    = shipping_address[:name]
          post[:C_ship_address] = shipping_address[:address1]
          post[:C_ship_city]    = shipping_address[:city]
          post[:C_ship_state]   = shipping_address[:state]
          post[:C_ship_zip]     = shipping_address[:zip]
          post[:C_ship_country] = shipping_address[:country]
        end
      end

      def add_transaction_data(post, money, options)
        add_amount(post, money)
        add_invoice(post, options)
        add_addresses(post, options)
        add_customer_data(post, options)
      end

      def commit(action, params)
        response = parse(ssl_post(self.live_url, post_data(action, params)))

        Response.new(success?(response), response[:message], response,
          :test => test?,
          :authorization => authorization_from(response),
          :avs_result => { :code => response[:avs_result] },
          :cvv_result => response[:cvv_result]
        )
      end

      def authorization_from(response)
        "#{response[:reference]};#{source}"
      end

      def success?(response)
        response[:success] == 'A'
      end

      def post_data(action, params = {})
        params[:M_id]  = @options[:login]
        params[:M_key] = @options[:password]
        params[:T_code] = TRANSACTIONS[action]

        params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
      end
    end
  end
end
