module ActiveShipping
  class Kunaki < Carrier
    self.retry_safe = true

    cattr_reader :name
    @@name = "Kunaki"

    URL = 'http://Kunaki.com/XMLService.ASP'

    CARRIERS = ["UPS", "USPS", "FedEx", "Royal Mail", "Parcelforce", "Pharos", "Eurotrux", "Canada Post", "DHL"]

    COUNTRIES = {
      'AR' => 'Argentina',
      'AU' => 'Australia',
      'AT' => 'Austria',
      'BE' => 'Belgium',
      'BR' => 'Brazil',
      'BG' => 'Bulgaria',
      'CA' => 'Canada',
      'CN' => 'China',
      'CY' => 'Cyprus',
      'CZ' => 'Czech Republic',
      'DK' => 'Denmark',
      'EE' => 'Estonia',
      'FI' => 'Finland',
      'FR' => 'France',
      'DE' => 'Germany',
      'GI' => 'Gibraltar',
      'GR' => 'Greece',
      'GL' => 'Greenland',
      'HK' => 'Hong Kong',
      'HU' => 'Hungary',
      'IS' => 'Iceland',
      'IE' => 'Ireland',
      'IL' => 'Israel',
      'IT' => 'Italy',
      'JP' => 'Japan',
      'LV' => 'Latvia',
      'LI' => 'Liechtenstein',
      'LT' => 'Lithuania',
      'LU' => 'Luxembourg',
      'MX' => 'Mexico',
      'NL' => 'Netherlands',
      'NZ' => 'New Zealand',
      'NO' => 'Norway',
      'PL' => 'Poland',
      'PT' => 'Portugal',
      'RO' => 'Romania',
      'RU' => 'Russia',
      'SG' => 'Singapore',
      'SK' => 'Slovakia',
      'SI' => 'Slovenia',
      'ES' => 'Spain',
      'SE' => 'Sweden',
      'CH' => 'Switzerland',
      'TW' => 'Taiwan',
      'TR' => 'Turkey',
      'UA' => 'Ukraine',
      'GB' => 'United Kingdom',
      'US' => 'United States',
      'VA' => 'Vatican City',
      'RS' => 'Yugoslavia',
      'ME' => 'Yugoslavia'
    }

    def find_rates(origin, destination, packages, options = {})
      requires!(options, :items)
      commit(origin, destination, options)
    end

    def valid_credentials?
      true
    end

    private

    def build_request(destination, options)
      country = COUNTRIES[destination.country_code]
      state_province = %w(US CA).include?(destination.country_code.to_s) ? destination.state : ''

      builder = Nokogiri::XML::Builder.new do |xml|
        xml.ShippingOptions do
          xml.AddressInfo do
            xml.Country(country)
            xml.State_Province(state_province)
            xml.PostalCode(destination.zip)
          end

          options[:items].each do |item|
            xml.Product do
              xml.ProductId(item[:sku])
              xml.Quantity(item[:quantity])
            end
          end
        end
      end
      builder.to_xml
    end

    def commit(origin, destination, options)
      request = build_request(destination, options)
      response = parse(ssl_post(URL, request, "Content-Type" => "text/xml"))

      RateResponse.new(success?(response), message_from(response), response,
                       rates: build_rate_estimates(response, origin, destination)
      )
    end

    def build_rate_estimates(response, origin, destination)
      response["Options"].collect do |quote|
        RateEstimate.new(origin, destination, carrier_for(quote["Description"]), quote["Description"],
                         total_price: quote["Price"],
                         currency: "USD"
        )
      end
    end

    def carrier_for(service)
      CARRIERS.dup.find { |carrier| service.to_s =~ /^#{carrier}/i } || service.to_s.split(" ").first
    end

    def parse(xml)
      response = {}
      response["Options"] = []

      document = Nokogiri.XML(sanitize(xml))

      response["ErrorCode"] = document.at('/Response/ErrorCode').text
      response["ErrorText"] = document.at('/Response/ErrorText').text

      document.xpath("Response/Option").each do |node|
        rate = {}
        rate["Description"] = node.at("Description").text
        rate["Price"]       = node.at("Price").text
        response["Options"] << rate
      end
      response
    end

    def sanitize(response)
      result = response.to_s
      result.gsub!("\r\n", "")
      result.gsub!(/<(\/)?(BODY|HTML)>/, '')
      result
    end

    def success?(response)
      response["ErrorCode"] == "0"
    end

    def message_from(response)
      response["ErrorText"]
    end
  end
end