#!/usr/bin/env ruby

require 'rhc/coverage_helper'

require 'rhc-common'

#
# print help
#
def p_usage(exit_code = 255)
    rhlogin = get_var('default_rhlogin') ? "Default: #{get_var('default_rhlogin')}" : "required"
    puts <<USAGE

Usage: rhc domain (<command> | --help) [<args>]
Manage a domain in rhcloud for a registered rhcloud user.

List of commands
  create            Bind a registered rhcloud user to a domain in rhcloud.
  show              Display domain information and list the applications within the domain 
  alter             Alter namespace (will change urls).
  status            Run a simple check on local configs and credentials to confirm tools are properly setup.
  destroy           Destroys the domain and any added ssh keys

List of arguments
  -l|--rhlogin      rhlogin      Red Hat login (RHN or OpenShift login) (#{rhlogin})
  -p|--password     password     RHLogin password (optional, will prompt)
  -n|--namespace    namespace    Namespace for your application(s) (alphanumeric - max #{RHC::DEFAULT_MAX_LENGTH} chars) (required for creating or destroying domain)
  -d|--debug                     Print Debug info
  -h|--help                      Show Usage info
  --config          path         Path of alternate config file
  --timeout         #            Timeout, in seconds, for the session

USAGE
exit exit_code
end


def get_args()
  args = ""
  $opt.each do |o, a|
    if a.length > 0 && a.to_s.strip.length == 0; a = "'#{a}'" end
    args += " --#{o} #{a}"
  end
  args
end

def validate_args(val_namespace=true, val_timeout=true)
  # If provided a config path, check it
  RHC::Config.check_cpath($opt)

  # Pull in configs from files
  $libra_server = get_var('libra_server')
  debug = get_var('debug') == 'false' ? nil : get_var('debug')

  $opt['rhlogin'] = get_var('default_rhlogin') unless $opt['rhlogin']
  p_usage if !RHC::check_rhlogin($opt['rhlogin'])

  p_usage if (val_namespace && !RHC::check_namespace($opt['namespace']))

  debug = $opt["debug"] ? true : false
  RHC::debug(debug)

  RHC::timeout($opt["timeout"], get_var('timeout')) if val_timeout
  RHC::connect_timeout($opt["timeout"], get_var('timeout')) if val_timeout

  $password = $opt['password'] ? $opt['password'] : RHC::get_password
end

def create_or_alter_domain(alter=false)
  validate_args()

  ssh_key_file_path = get_kfile(false)
  ssh_pub_key_file_path = get_kpfile(ssh_key_file_path, alter)

  ssh_config = "#{ENV['HOME']}/.ssh/config"
  ssh_config_d = "#{ENV['HOME']}/.ssh/"

  # Check to see if a ssh_key_file_path exists, if not create it.
  if File.readable?(ssh_key_file_path)
      puts "OpenShift key found at #{ssh_key_file_path}.  Reusing..."
  else
      puts "Generating OpenShift ssh key to #{ssh_key_file_path}"
      w = RHC::SSHWizard.new($opt['rhlogin'], $password)
      w.run
  end
  
  ssh_keyfile_contents = File.open(ssh_pub_key_file_path).gets.chomp.split(' ')
  ssh_key = ssh_keyfile_contents[1]
  ssh_key_type = ssh_keyfile_contents[0]
  
  data = {'namespace' => $opt['namespace'],
          'rhlogin' => $opt['rhlogin']}
  
  # send the ssh key and key type only in case of domain creation
  # key updates will be handled by the 'rhc sshkey update' command
  if !alter
    data[:ssh] = ssh_key
    data[:key_type] = ssh_key_type
  end

  if alter
    data[:alter] = true
    not_found_message = "A user with rhlogin '#{$opt['rhlogin']}' does not have a registered domain.  Be sure to run 'rhc domain create' first."
    user_info = RHC::get_user_info($libra_server, $opt['rhlogin'], $password, RHC::Config.default_proxy, true, not_found_message)
  end
  if @mydebug
    data[:debug] = true
  end
  RHC::print_post_data(data)
  json_data = RHC::generate_json(data)
  
  url = URI.parse("https://#{$libra_server}/broker/domain")
  response = RHC::http_post(RHC::Config.default_proxy, url, json_data, $password)
  
  if response.code == '200'
      begin
          json_resp = RHC::json_decode(response.body)
          RHC::print_response_success(json_resp)
          json_rhlogininfo = RHC::json_decode(json_resp['data'])
          add_rhlogin_config(json_rhlogininfo['rhlogin'], json_rhlogininfo['uuid'])
          if !alter
                        puts <<EOF
Creation successful

You may now create an application.

EOF
          else
              app_info = user_info['app_info']
              dns_success = true
              if !app_info.empty? && $opt['namespace'] != user_info['user_info']['domains'][0]['namespace']
                #
                # Confirm that the host(s) exist in DNS
                #
                puts "Now your new domain name(s) are being propagated worldwide (this might take a minute)..."
                # Allow DNS to propogate
                sleep 15
                app_info.each_key do |appname|
                    fqdn = "#{appname}-#{$opt['namespace']}.#{user_info['user_info']['rhc_domain']}"
                    
                    # Now start checking for DNS
                    loop = 0
                    sleep_time = 2
                    while loop < RHC::MAX_RETRIES && !RHC::hostexist?(fqdn)
                        sleep sleep_time
                        loop+=1
                        puts "  retry # #{loop} - Waiting for DNS: #{fqdn}"
                        sleep_time = RHC::delay(sleep_time)
                    end
                    
                    if loop >= RHC::MAX_RETRIES
                        puts "Host could not be found: #{fqdn}"
                        dns_success = false
                    end
                end
                puts "You can use 'rhc domain show' to view any url changes.  Be sure to update any links"
                puts "including the url in your local git config: <local_git_repo>/.git/config"
              end
              if dns_success
                puts "Alteration successful."
              else
                puts "Alteration successful but at least one of the urls is still updating in DNS."
              end
              puts ""
          end
          exit 0
      rescue RHC::JsonError
          RHC::print_response_err(response)
      end
  else
      RHC::print_response_err(response)
  end
  exit 1
end

def destroy_domain()
  validate_args(true, false)

  url = URI.parse("https://#{$libra_server}/broker/domain")
  data = {}
  data[:rhlogin] = $opt['rhlogin']
  data[:delete] = true
  data[:namespace] = $opt['namespace']

  RHC::print_post_data(data)
  json_data = RHC::generate_json(data)
  
  response = RHC::http_post(RHC::Config.default_proxy, url, json_data, $password)
  
  if response.code == '200'
    begin
      json_resp = RHC::json_decode(response.body)
      RHC::update_server_api_v(json_resp)
      RHC::print_response_success(json_resp)
      puts "Success"
      exit 0
    rescue RHC::JsonError
      RHC::print_response_err(response)
    end
  else
    RHC::print_response_err(response)
  end
  puts "Failure"
  exit 1
end

def show_domain_info()
  validate_args(false, true)
  
  user_info = RHC::get_user_info($libra_server, $opt['rhlogin'], $password, RHC::Config.default_proxy, true)
  
  domains = user_info['user_info']['domains']
  num_domains = domains.length

  puts ""
  puts "User Info"
  puts "========="

  if num_domains == 0
    puts "Namespace: No namespaces found. You can use 'rhc domain create -n <namespace>' to create a namespace for your applications."
  elsif num_domains == 1
    puts "Namespace: #{domains[0]['namespace']}"
  else
    domains.each_index { |i| puts "Namespace(#{i}): #{domains[i]['namespace']}" }
  end

  #puts "    UUID: #{user_info['user_info']['uuid']}"
  puts "  RHLogin: #{user_info['user_info']['rhlogin']}"

  puts "\n\n" 

  puts "Application Info"
  puts "================"
  unless user_info['app_info'].empty?
    user_info['app_info'].each do |key, val|
        puts key
        puts "    Framework: #{val['framework']}"
        puts "     Creation: #{val['creation_time']}"
        puts "         UUID: #{val['uuid']}"
        puts "      Git URL: ssh://#{val['uuid']}@#{key}-#{user_info['user_info']['domains'][0]['namespace']}.#{user_info['user_info']['rhc_domain']}/~/git/#{key}.git/"
        puts "   Public URL: http://#{key}-#{user_info['user_info']['domains'][0]['namespace']}.#{user_info['user_info']['rhc_domain']}/"
        if val['aliases'] && !val['aliases'].empty?
          puts "      Aliases: #{val['aliases'].join(', ')}"
        end
        puts ""
        puts " Embedded: "
        if val['embedded'] && !val['embedded'].empty? 
            val['embedded'].each do |embed_key, embed_val|
                if embed_val.has_key?('info') && !embed_val['info'].empty?
                    puts "      #{embed_key} - #{embed_val['info']}"
                else
                    puts "      #{embed_key}"
                end
            end
        else
            puts "      None"
        end
        puts ""
    end
  else
    puts "No applications found.  You can use 'rhc app create' to create new applications."
  end
end

def check_domain_status
  $opt['rhlogin'] = get_var('default_rhlogin') unless $opt['rhlogin']
  p_usage if !RHC::check_rhlogin($opt['rhlogin'])

  system("rhc-chk #{get_args} 2>&1")
  if ($? != 0); exit 1 end
end


begin
  argv_c = ARGV.clone

  if ARGV[0] =~ /^(create|alter|destroy)$/
    ARGV.shift
    opts = GetoptLong.new(
        ["--debug", "-d", GetoptLong::NO_ARGUMENT],
        ["--help",  "-h", GetoptLong::NO_ARGUMENT],
        ["--rhlogin", "-l", GetoptLong::REQUIRED_ARGUMENT],
        ["--password", "-p", GetoptLong::REQUIRED_ARGUMENT],
        ["--namespace", "-n", GetoptLong::REQUIRED_ARGUMENT],
        ["--config", GetoptLong::REQUIRED_ARGUMENT],
        ["--timeout", GetoptLong::REQUIRED_ARGUMENT]
    )
  elsif ARGV[0] =~ /^(status|show)$/
    ARGV.shift
    opts = GetoptLong.new(
        ["--debug", "-d", GetoptLong::NO_ARGUMENT],
        ["--help",  "-h", GetoptLong::NO_ARGUMENT],
        ["--rhlogin", "-l", GetoptLong::REQUIRED_ARGUMENT],
        ["--password", "-p", GetoptLong::REQUIRED_ARGUMENT],
        ["--config", GetoptLong::REQUIRED_ARGUMENT],
        ["--timeout", GetoptLong::REQUIRED_ARGUMENT]
    )
  else
    # if the user just enters "rhc domain", don't throw an error
    # let it be handled by the "rhc domain show" command
    if ARGV[0].to_s.strip.length == 0
      opts = []
    else
      opts = GetoptLong.new(
        ["--help",  "-h", GetoptLong::NO_ARGUMENT]
      )

      unless ARGV[0] =~ /^(help|-h|--help)$/
        puts "Missing or invalid command!" 
        # just exit at this point
        # printing the usage description will be handled in the rescue
        exit 255
      end
    end
  end

  $opt = {}
  opts.each do |o, a|
    $opt[o[2..-1]] = a.to_s
  end
rescue Exception => e
  p_usage
end

p_usage 0 if $opt["help"]

case argv_c[0]
when "create"
  create_or_alter_domain(false)
when "alter"
  create_or_alter_domain(true)
when "status"
  check_domain_status
when "show", nil
  show_domain_info
when "destroy"
  destroy_domain
when "-h", "--help", "help", nil
  p_usage
else
  puts "Invalid command!"
  p_usage
end

exit 0
