require 'paint'
require 'optparse'

# A Ruby class to call the App42 REST API.

module App42
  module Command
    class Client

      # read access
      attr_reader :kclass
      attr_reader :command
      attr_reader :args
      attr_reader :options

      # @return [Object]
      def initialize(arg = [])
        @args = args
        @options = {}
        @should_exit = true
        @exception_msg = nil
      end

      # pre load all commands file
      def load_command_file
        Dir[File.join(File.dirname(__FILE__), "*.rb")].each { |file| require file }
      end

      # @param args
      # call for require files
      # Initiate client object
      def self.app42(*args )
        begin
          action = args.shift.strip rescue 'help'
          App42::Command::Client.new.load_command_file
          App42::Command::Client.new.start(action, args)
        rescue Interrupt => e 
          @exception_msg = e.message
        rescue OptionParser::MissingArgument => e
          @exception_msg = e.message
        rescue OptionParser::InvalidOption => e 
          @exception_msg = e.message
        rescue OptionParser::AmbiguousOption => e  
          @exception_msg = e.message
        rescue => error
          @exception_msg = e.message
        ensure
          puts Paint["#{@exception_msg}", :red] 
          @should_exit = true if @should_exit.nil?
          exit @should_exit   
        end

      end

      # Collect all the available options for all commands
      # Some duplicates exists to capture all scenarios
      def parse_options(command, args)
        opts_parser = OptionParser.new do |opts|
          opts.banner = "\nAvailable options:\n\n"

          opts.on('--apiKey API_KEY')         { |api|      @options[:api] = api }
          opts.on('--secretKey SECRET_KEY')   { |secret|   @options[:secret] = secret }
          opts.on('-a API_KEY')               { |api|      @options[:api] = api }
          opts.on('-s SECRET_KEY')            { |secret|   @options[:secret] = secret }

          opts.on('--app NAME')               { |name|     @options[:name] = name }
          opts.on('--name NAME')              { |name|     @options[:name] = name }

          opts.on('--instance INSTANCE')      { |instance| @options[:instance] = instance }
          opts.on('-i INSTANCE')              { |instance| @options[:instance] = instance }

          opts.on('--kontena KONTENA')        { |kontena| @options[:kontena] = kontena }
          opts.on('-i KONTENA')               { |kontena| @options[:kontena] = kontena }

          opts.on('--service SERVICE')        { |service| @options[:service] = service }
          opts.on('-s SERVICE')               { |service| @options[:service] = service }

          opts.on('--setup SETUP')            { |setup| @options[:setup] = setup }
          opts.on('-s SETUP')                 { |setup| @options[:setup] = setup }

          opts.on('-h', '--help')             {            puts "#{App42::Base::Help.usage(command)}\n"; exit! }
        end
        opts_parser.parse!(args)
        self
      end

      # @param [Object] command
      # @param [Object] args
      def start(command, args = [])
        if is_available?(command)
          parse_options(command.downcase, args)
          execute command.downcase
          cmd = App42::Command.const_get(@kclass.to_s.capitalize)
          begin
            cmd.new(@options).send(@command)
          rescue Interrupt
            puts Paint[" Command cancelled.", :red]
            exit!
          rescue Exception => e
            puts e
          end
        elsif command == 'help'
          send(command.downcase)
        else
          puts Paint["app42: Unknown command [#{command}]", :red]
          App42::Base::Help.how_to
        end
      end


      # @return true OR false
      def is_available? command
        App42::Base::APP42_COMMAND.include?(command.downcase)
      end

      # choose action respective of class
      def execute command
        case command
          when 'version'
            set_cmd(:config, :version)

          when 'list'
            set_cmd(:config, :list)

          when 'apps'
            set_cmd(:app, :apps)

          when 'deploy'
            set_cmd(:app, :deploy)

          when 'setupinfra'
            set_cmd(:app, :setup_infra)

          when 'update'
            set_cmd(:app, :update)

          when 'keys'
            set_cmd(:user, :keys)

          when 'clearkeys'
            set_cmd(:user, :clear)

          when 'addkeys'
            set_cmd(:user, :add)

          when 'scale'
            set_cmd(:app, :scale)

          when 'descale'
            set_cmd(:app, :descale)

          when 'appstate'
            set_cmd(:info, :state)

          when 'appinfo'
            set_cmd(:info, :info)

          when 'logs'  
            set_cmd(:info, :logs)
 
          when 'start'
            set_cmd(:app, :start)

          when 'stop'
            set_cmd(:app, :stop)

          when 'restart'
            set_cmd(:app, :restart)

          when 'deleteinfra'
            set_cmd(:app, :delete)

          when 'releases'
            set_cmd(:info, :releases)

          when 'iaasproviders'
            set_cmd(:config, :iaas_providers)

          when 'frameworks'
            set_cmd(:config, :frameworks)

          when 'runtimes'
            set_cmd(:config, :runtimes)

          when 'vmconfigs'
            set_cmd(:config, :memory)

          when 'app42-update'
            set_cmd(:config, :update)

          when 'supportedservices'
            set_cmd(:service, :app42pass_services)

          when 'activities'
            set_cmd(:info, :activities)  

          when 'services'
            set_cmd(:service, :services)
          
          when 'serviceinfo'
            set_cmd(:service, :info)
            
          when 'createservice'
            set_cmd(:service, :create)
            
          when 'deleteservice'
            set_cmd(:service, :delete)

          when 'startservice'
            set_cmd(:service, :start)
            
          when 'restartservice'
            set_cmd(:service, :restart)
            
          when 'stopservice'
            set_cmd(:service, :stop)

          when 'scaleservice'
            set_cmd(:service, :vscale)

          when 'descaleservice'
            set_cmd(:service, :vdescale)  

          when 'uploadbackup'
            set_cmd(:service, :uploadbackup)          

          when 'resetservicepassword'
            set_cmd(:service, :reset_pass) 
                                
          when 'bindip'
            set_cmd(:service, :service_bind)

          when 'unbindip'
            set_cmd(:service, :service_unbind)
            
          when 'bindinfo'
            set_cmd(:service, :service_bindInfo)

          when 'setupbpaas'
            set_cmd(:setup, :setup_cloud_api)

          when 'startbpaas'
            set_cmd(:setup, :start)

          when 'stopbpaas'
            set_cmd(:setup, :stop) 

          when 'upgradebpaas'
            set_cmd(:setup, :upgrade_cloud_api)     

          when 'deletesetup'
            set_cmd(:setup, :delete_cloud_api)

          when 'setupinfo'
            set_cmd(:setup, :info)

          when 'setups'
            set_cmd(:setup, :setups)                    

          else
          puts Paint["app42: Unknown command [#{action}]", :red]
          App42::Base::Help.how_to
        end
      end

      # Set class and respective methods
      def set_cmd(kclass, command)
        @kclass = kclass
        @command = command
      end

      # invoke help options
      def help
        App42::Base::Help.commands
      end

    end
  end
end