#!/usr/bin/env ruby

require 'rhc/coverage_helper'

require 'rhc-common'

RHC::Helpers.deprecated_command('rhc port-forward',true)

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

Usage: rhc port-forward
Forward remote ports to the workstation

  -l|--rhlogin   rhlogin    OpenShift login (#{rhlogin})
  -a|--app                  Target application (required)
  -p|--password  password   Password (optional, will prompt)
  -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

begin
    opts = GetoptLong.new(
        ["--debug", "-d", GetoptLong::NO_ARGUMENT],
        ["--help",  "-h", GetoptLong::NO_ARGUMENT],
        ["--app",  "-a", GetoptLong::REQUIRED_ARGUMENT],
        ["--rhlogin",  "-l", GetoptLong::REQUIRED_ARGUMENT],
        ["--config", GetoptLong::REQUIRED_ARGUMENT],
        ["--password",  "-p", GetoptLong::REQUIRED_ARGUMENT],
        ["--timeout", GetoptLong::REQUIRED_ARGUMENT]
    )
    opt = {}
    opts.each do |o, a|
        opt[o[2..-1]] = a.to_s
    end
rescue Exception => e
  #puts e.message
    p_usage
end

# 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')

p_usage 0 if opt['help']

p_usage if !opt['app'] || 0 != ARGV.length

debug = true if opt['debug']

RHC::debug(debug)

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

opt['rhlogin'] = get_var('default_rhlogin') unless opt['rhlogin']

if !RHC::check_rhlogin(opt['rhlogin'])
    p_usage
end

password = opt['password']
if !password
  password = RHC::get_password
end

user_info = RHC::get_user_info(libra_server, opt['rhlogin'], password, RHC::Config.default_proxy, false)

app_name = opt['app']

unless user_info['app_info'][app_name]
    puts
    puts "Could not find app '#{app_name}'.  Please run rhc-domain-info to get a list"
    puts "of your current running applications"
    puts
    exit 101
end

if (user_info['app_info'][app_name].has_key?('embedded') && user_info['app_info'][app_name]['embedded'].keys.any?{ |k| k =~ /\Ahaproxy/ })
  puts
  puts "This utility does not currently support scaled applications.\n"
  puts "You will need to set up port forwarding manually.\n"
  puts
  exit 101
end

# mock for windows
if defined?(UNIXServer) != 'constant' or UNIXServer.class != Class
  class UNIXServer; end
end

app_uuid = user_info['app_info'][app_name]['uuid']
namespace = user_info['user_info']['domains'][0]['namespace']
rhc_domain = user_info['user_info']['rhc_domain']
ssh_host = "#{app_name}-#{namespace}.#{rhc_domain}"

puts "Checking available ports..."

ip_and_port_simple_regex = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\:[0-9]{1,5}/

hosts_and_ports = []
hosts_and_ports_descriptions = []
scaled_uuids = []

puts "Using #{app_uuid}@#{ssh_host}..." if debug

Net::SSH.start(ssh_host, app_uuid) do |ssh|

  ssh.exec! "rhc-list-ports" do |channel, stream, data|

    if stream == :stderr

      data.lines { |line|

        line = line.chomp

        if line.downcase =~ /permission denied/
          puts line
          exit 1
        end

        if line.index(ip_and_port_simple_regex)
          hosts_and_ports_descriptions << line
        end
      }

    else

      data.lines { |line|

        line = line.chomp

        if line.downcase =~ /scale/
          scaled_uuid = line[5..-1]
          scaled_uuids << scaled_uuid

        else
          if ip_and_port_simple_regex.match(line)
            hosts_and_ports << line
          end
        end
      }

    end

  end

  scaled_uuids.each { |scaled_uuid|

    puts "Using #{scaled_uuid}@#{ssh_host} (scaled instance)..." if debug

    Net::SSH.start(ssh_host, scaled_uuid) do |ssh|

      ssh.exec! "rhc-list-ports" do |channel, stream, data|
        if stream == :stderr
          data.lines { |line|

            line = line.chomp
            if line.downcase =~ /permission denied/
              puts line
              exit 1
            end
            if line.index(ip_and_port_simple_regex)
              hosts_and_ports_descriptions << line
            end
          }
        else
          data.lines { |line|

            line = line.chomp
            if ip_and_port_simple_regex.match(line)
              hosts_and_ports << line
            end
          }
        end
      end
    end
  }

  if hosts_and_ports.length == 0
    puts
    puts "No available ports to forward"
    exit 102
  end

  puts

  hosts_and_ports_descriptions.each { |description| puts "Binding #{description}..." }

  begin

    Net::SSH.start(ssh_host, app_uuid) do |ssh|
      puts "Use ctl + c to stop"
      hosts_and_ports.each do |host_and_port|
        host, port = host_and_port.split(/:/)
        ssh.forward.local(host, port.to_i, host, port.to_i)
      end
      ssh.loop { true }
    end

  rescue Interrupt
    puts
    puts "Terminating..."
    exit 0

  rescue Exception => e
    puts
    puts e.message if debug
    puts "Error trying to forward ports. You can try to forward manually by running:"
    ssh_cmd = "ssh -N "
    hosts_and_ports.each { |port| ssh_cmd << "-L #{port}:#{port} " }
    ssh_cmd << "#{app_uuid}@#{app_name}-#{namespace}.#{rhc_domain}"
    puts
    puts ssh_cmd
    puts
    exit 1
  end

end

# never
exit 1
