gem 'nap' # gives you rest...
require 'rest'
require 'active_support'
require 'active_support/version'
%w{
  active_support/json
}.each do |active_support_3_requirement|
  require active_support_3_requirement
end if ActiveSupport::VERSION::MAJOR == 3

module EY
  module Metadata
    # An adapter that reads from the public EngineYard Cloud API (https://cloud.engineyard.com). Available from anywhere.
    #
    # See README for what environment variables and/or files you need to have in place for this to work.
    class EngineYardCloudApi
      URL = 'https://cloud.engineyard.com/api/v2/environments'
      
      # Currently the same as the SSH username.
      def database_username
        data['ssh_username']
      end
      
      # The username for connecting by SSH.
      def ssh_username
        data['ssh_username']
      end
      
      # Currently the same as the app name, at least for recently-created environments.
      #
      # It used to be named after the environment.
      def database_name
        data['apps'][0]['name']
      end
      
      # The hostname of the database host.
      def database_host
        db_master
      end

      # The public hostname of the db_master.
      #
      # If you're on a solo app, it counts the solo as the app_master.
      def db_master
        if x = data['instances'].detect { |i| i['role'] == 'db_master' }
          x['public_hostname']
        else
          solo
        end
      end
      
      # The public hostnames of all the app servers.
      #
      # If you're on a solo app, it counts the solo as an app server.
      def app_servers
        data['instances'].select { |i| %w{ app_master app solo }.include? i['role'] }.map { |i| i['public_hostname'] }.sort
      end
      
      # The public hostnames of all the db servers.
      #
      # If you're on a solo app, it counts the solo as a db server.
      def db_servers
        data['instances'].select { |i| %w{ db_master db_slave solo }.include? i['role'] }.map { |i| i['public_hostname'] }.sort
      end
      
      # The public hostnames of all the utility servers.
      #
      # If you're on a solo app, it counts the solo as a utility.
      def utilities
        data['instances'].select { |i| %w{ util solo }.include? i['role'] }.map { |i| i['public_hostname'] }.sort
      end
      
      # The public hostnames of all the app slaves.
      def app_slaves
        data['instances'].select { |i| %w{ app }.include? i['role'] }.map { |i| i['public_hostname'] }.sort
      end
  
      # The public hostnames of all the db slaves.
      def db_slaves
        data['instances'].select { |i| %w{ db_slave }.include? i['role'] }.map { |i| i['public_hostname'] }.sort
      end
      
      # The public hostname of the app_master.
      #
      # If you're on a solo app, it counts the solo as the app_master.
      def app_master
        if x = data['instances'].detect { |i| i['role'] == 'app_master' }
          x['public_hostname']
        else
          solo
        end
      end

      # The public hostname of the solo.
      def solo
        if x = data['instances'].detect { |i| i['role'] == 'solo' }
          x['public_hostname']
        end
      end
      
      # The name of the EngineYard AppCloud environment.
      def environment_name
        data['name']
      end
      
      # The stack in use, like nginx_passenger.
      def stack_name
        data['stack_name']
      end
      
      # The git repository that you told EngineYard to use for this application.
      def repository_uri
        data['apps'][0]['repository_uri']
      end
      
      def data_loaded?
        defined?(@data) and @data.is_a? Hash
      end
      
      def data
        return @data if data_loaded?
        raw_json = REST.get(URL, 'X-EY-Cloud-Token' => EY::Metadata.ey_cloud_token).body
        raw_data = ActiveSupport::JSON.decode raw_json
        matching_environment_hshs = raw_data['environments'].select do |environment_hsh|
          if EY::Metadata.environment_name
            environment_hsh['name'] == EY::Metadata.environment_name
          else
            environment_hsh['apps'].any? { |app_hsh| app_hsh['repository_uri'] == EY::Metadata.repository_uri }
          end
        end
        raise RuntimeError, "[engineyard-metadata gem] Found too many environments: #{matching_environment_hshs.map { |hsh| hsh['name'] }.join(', ')}" if matching_environment_hshs.length > 1
        @data = matching_environment_hshs[0]
        raise RuntimeError, "[engineyard-metadata gem] Couldn't find an EngineYard environment with the repository uri #{repository_uri}" unless data_loaded?
        @data
      end
    end
  end
end