#!/usr/bin/env ruby

require 'rhc/coverage_helper'

require 'rhc-common'

RHC::Helpers.deprecated_command('rhc domain',true)

#
# 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).
  destroy           Destroys the domain and any added ssh keys

List of arguments
  -l|--rhlogin      rhlogin      OpenShift login (#{rhlogin})
  -p|--password     password     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 "  OpenShift login: #{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

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] =~ /^(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 "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
