module ActiveMerchant #:nodoc:
  module Billing #:nodoc:
    class GarantiGateway < Gateway
      URL = 'https://ccpos.garanti.com.tr/servlet/cc5ApiServer'

      # The countries the gateway supports merchants from as 2 digit ISO country codes
      self.supported_countries = ['US','TR']

      # The card types supported by the payment gateway
      self.supported_cardtypes = [:visa, :master, :american_express, :discover]

      # The homepage URL of the gateway
      self.homepage_url = 'https://ccpos.garanti.com.tr/ccRaporlar/garanti/ccReports'

      # The name of the gateway
      self.display_name = 'Garanti Sanal POS'

      self.default_currency = 'TRL'

      CURRENCY_CODES = {
        'YTL' => 949,
        'TRL' => 949,
        'USD' => 840,
        'EUR' => 978
      }


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

      def purchase(money, credit_card, options = {})
        commit(money, build_sale_request(money, credit_card, options))
      end

      def authorize(money, credit_card, options = {})
        commit(money, build_authorize_request(money, credit_card, options))
      end

      def capture(money, reference, options = {})
        commit(money, build_capture_request(money,reference,options))
      end

      private

      def build_xml_request(transaction_type,&block)
        xml = Builder::XmlMarkup.new
        xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"

        xml.tag! 'CC5Request' do
          xml.tag! 'Name', @options[:login]
          xml.tag! 'Password', @options[:password]
          xml.tag! 'ClientId', @options[:client_id]
          xml.tag! 'Mode', if test? then 'R' else 'P' end
          xml.tag! 'Type', transaction_type

          if block_given?
            yield xml
          else
            xml.target!
          end
        end
      end

      def build_sale_request(money, credit_card, options)
        build_xml_request('Auth') do |xml|
          add_customer_data(xml,options)
          add_order_data(xml,options)
          add_credit_card(xml, credit_card)
          add_addresses(xml, options)

          xml.tag! 'Total', amount(money)
          xml.tag! 'Currency', currency_code(options[:currency])

          xml.target!
        end
      end

      def build_authorize_request(money, credit_card, options)
         build_xml_request('PreAuth') do |xml|
          add_customer_data(xml,options)
          add_order_data(xml,options)
          add_credit_card(xml, credit_card)
          add_addresses(xml, options)

          xml.tag! 'Total', amount(money)
          xml.tag! 'Currency', currency_code(options[:currency])

          xml.target!

        end
      end

      def build_capture_request(money, reference, options = {})
        build_xml_request('PostAuth') do |xml|
          add_customer_data(xml,options)
          xml.tag! 'OrderId', reference
          xml.tag! 'Total', amount(money)
          xml.tag! 'Currency', currency_code(options[:currency])

          xml.target!
        end
      end

      def build_void_request(reference, options = {})
         build_xml_request('Void') do |xml|
          add_customer_data(xml,options)
          xml.tag! 'OrderId', reference
          xml.tag! 'Total', amount(money)
          xml.tag! 'Currency', currency_code(options[:currency])

          xml.target!
        end
      end

      def build_credit_request(money, reference, options = {})
        build_xml_request('Credit') do |xml|
          add_customer_data(xml,options)
          xml.tag! 'OrderId', reference
          xml.tag! 'Total', amount(money)
          xml.tag! 'Currency', currency_code(options[:currency])

          xml.target!
        end
      end

      def add_customer_data(xml, options)
        xml.tag! 'IPAddress', options[:ip_]
        xml.tag! 'Email', options[:email]
      end

      def add_order_data(xml,options)
        xml.tag! 'OrderId', options[:order_id]
        xml.tag! 'GroupId', nil
        xml.tag! 'TransId', nil
      end

      def add_credit_card(xml, credit_card)
        xml.tag! 'Number', credit_card.number
        xml.tag! 'Expires', [format_exp(credit_card.month),format_exp(credit_card.year)].join('/')
        xml.tag! 'Cvv2Val', credit_card.verification_value
      end

      def format_exp(value)
        format(value, :two_digits)
      end

      def add_addresses(xml,options)
        if billing_address = options[:billing_address] || options[:address]
          xml.tag! 'BillTo' do
            xml.tag! 'Name', billing_address[:name]
            xml.tag! 'Street1', billing_address[:address1]
            xml.tag! 'Street2', billing_address[:address2]
            xml.tag! 'City', billing_address[:city]
            xml.tag! 'StateProv', billing_address[:state]
            xml.tag! 'PostalCode', billing_address[:zip]
            xml.tag! 'Country', billing_address[:country]
            xml.tag! 'Company', billing_address[:company]
            xml.tag! 'TelVoice', billing_address[:phone]
          end
        end

        if shipping_address = options[:shipping_address]
          xml.tag! 'ShipTo' do
            xml.tag! 'Name', shipping_address[:name]
            xml.tag! 'Street1', shipping_address[:address1]
            xml.tag! 'Street2', shipping_address[:address2]
            xml.tag! 'City', shipping_address[:city]
            xml.tag! 'StateProv',shipping_address[:state]
            xml.tag! 'PostalCode',shipping_address[:zip]
            xml.tag! 'Country', shipping_address[:country]
            xml.tag! 'Company', shipping_address[:company]
            xml.tag! 'TelVoice', shipping_address[:phone]
          end
        end
      end

      def currency_code(currency)
        CURRENCY_CODES[currency] || CURRENCY_CODES[default_currency]
      end

      def commit(money,request)
        raw_response = ssl_post(URL,"DATA=" + request)
        response = parse(raw_response)

        success = success?(response)

        Response.new(success,
                     success ? 'Approved' : 'Declined',
                     response,
                     :test => test?,
                     :authorization => response[:order_id])
      end

      def parse(body)
        xml = REXML::Document.new(body)

        response = {}
        xml.root.elements.to_a.each do |node|
          parse_element(response, node)
        end
        response
      end

      def parse_element(response, node)
        if node.has_elements?
          node.elements.each{|element| parse_element(response, element) }
        else
          response[node.name.underscore.to_sym] = node.text
        end
      end

      def success?(response)
        response[:response] == "Approved"
      end

    end
  end
end