require 'fileutils'
require 'app42/command/base'
require 'terminal-table'

module App42
  module Command
    class App < Base

      # collect all required attributes for new VM spawn
      def setup_infra
        app_name = get_app_name_and_check_app_url_availability
        vm_type = get_vm_types
        iaas = get_iaas_providers
        app_name, source_url = get_app_source app_name, iaas, vm_type
      end

      # collect all required attributes for app deploy
      def deploy
        @options[:name] = get_app_name if @options[:name].nil?
        response = interactive_get 'info', 'app/uploadtypes' if is_app_exist? @options[:name]
        app_source_type = response['sourceTypes']
         
        if response['sourceTypes'].count > 1
          source_type = ask_app_source response['sourceTypes']
          if source_type == 'Source'
            git_url = get_git_url
            status = upload_binary @options[:name], source_type, git_url if validate_git_url git_url
          else
            status = get_binary_url @options[:name]
          end
        else
          status = get_binary_url @options[:name]
        end
        exit! if status
      end

      # collect all required attributes for app update
      def update
        @options[:name] = get_app_name if @options[:name].nil?
        response = interactive_get 'info', 'app/uploadtypes' if is_app_exist? @options[:name]
        app_source_type = response['sourceTypes']
         
        if response['sourceTypes'].count > 1
          source_type = ask_app_source response['sourceTypes']
          if source_type == 'Source'
            status = update_binary @options[:name]
          else
            status = get_binary_url @options[:name]
          end
        else
          status = get_binary_url @options[:name]
        end
        exit! if status
      end      

      # @param app_name
      # 
      # collect app source type
      #
      # pass app name, app source and source url to binary upload methods
      def get_binary_url app_name
        app_source, source_url = collect_app_source app_name
        status = upload_binary app_name, app_source, source_url
        return status
      end

      # @param app_name
      # @param iaas
      # @param vm_type
      # @return host name
      def collect_vm_details app_name, iaas, vm_type
        runtime = get_runtime
        framework = get_framework iaas, vm_type, runtime
        webserver = get_webserver iaas, vm_type, runtime, framework
        os = get_os_for_app iaas, vm_type, runtime, framework, webserver
        # FIXME, may be configure out later
        # instance = get_instance 'new_vm'
        kontena = get_vmconfig
        setup_infra_res = App42::Command::Base.new.create_infrastructure app_name, iaas, vm_type, runtime, framework, webserver, os, kontena
        exit! if setup_infra_res
      end

      # @param deployment path
      # @param app_name
      # @return binary name
      def is_binary_exist? source_url, app_name
        path = escape_path(source_url)
        app_file_name = nil
        @binary_retry ||= 1
        no_of_file = []
        Dir["#{path}/*"].collect {|f| app_file_name = f and no_of_file << f  if f.include?('.zip') || f.include?('.war') || f.include?('.tar.gz') || f.include?('.gzip') }
        if no_of_file.count > 1
          message "#{Message::MORE_THAN_ONE_BINARY}", true, 'red'
          app_file_name = input "Please Select Binary", no_of_file, true
        elsif app_file_name.nil?
          message "#{Message::NO_BINARY}", true, 'red'
          exit! if (@binary_retry +=1 ) >= 4
          get_binary_url app_name
        end
        return app_file_name
      end

      # ask existing app url for deploy
      def ask_existing_app_url
        input "Enter App URL", [], true
      end

      # @param app_source_url
      # @return upload type
      def ask_app_source app_source_type
        input "Choose Upload Type", app_source_type, true
      end

      def ask_scale_type #:ndoc:
        input "Choose Scale Type", App42::SCALE_TYPE, true
      end

      # @param binary/git
      # @return path
      def get_source_path app_source
        path = ask(Paint["#{app_source.capitalize} Deployment Path", :cyan])
        return path.strip 
      end

      # collect git URL from user
      def get_git_url(prompt = Paint["Enter Git URL?", :cyan])
        ask(prompt) {|q| q.each = true}
      end
      
      # read +app name+ and number of +instance+ from user
      # then scale app by no of instance
      def scale
        @options[:name] = get_app_name if @options[:name].nil?
        scale_type = ask_scale_type
        if scale_type == App42::SCALE_TYPE.first
          hscale @options[:name]
        else
          vscale @options[:name]
        end
      end

      # read +app name+ and number of +instance+ from user
      # then descale app by no of instance
      def descale
        @options[:name] = get_app_name if @options[:name].nil?
        scale_type = ask_scale_type
        if scale_type == App42::SCALE_TYPE.first
          hdescale @options[:name]
        else
          vdescale @options[:name]
        end
      end

      # read +app name+ and number of +instance+ from user
      # then scale app by no of instance
      def hscale app_name
        @options[:instance] = get_instance "Horizontal scale" if is_app_exist? app_name and @options[:instance].nil?
        scale_or_descal_res = scale_or_descale_app "scale", @options[:instance], app_name
        exit! if scale_or_descal_res
      end

      # read +app name+ and number of +instance+ from user
      # then descale app by no of instance
      def hdescale app_name
        @options[:instance] = get_instance "Horizontal descale" if is_app_exist? app_name and @options[:instance].nil?
        scale_or_descal_res = scale_or_descale_app "descale", @options[:instance], app_name
        exit! if scale_or_descal_res
      end

      # read +app name+ and number of +instance+ from user
      # then vertically scale app by no of instance
      def vscale app_name
        @options[:kontena] = get_kontena "Vertical scale" if is_app_exist? app_name and @options[:kontena].nil?
        vscale_or_vdescal_res = vscale_or_vdescale_app __method__, @options[:kontena], app_name
        exit! if vscale_or_vdescal_res
      end

      # read +app name+ and number of +instance+ from user
      # then vertically descale app by no of instance
      def vdescale app_name
        @options[:kontena] = get_kontena "Vertical descale" if is_app_exist? app_name and @options[:kontena].nil?
        vscale_or_vdescal_res = vscale_or_vdescale_app __method__, @options[:kontena], app_name
        exit! if vscale_or_vdescal_res
      end

      # app42 start
      #
      # start the app, return true or error code/message
      #
      def start
        @options[:name] = get_app_name if @options[:name].nil?
        app_operation_req = app_operation __method__, @options[:name] if is_app_exist? @options[:name]
        exit! if app_operation_req
      end

      # app42 stop
      #
      # stop the app, return true or error code/message
      #
      def stop
        @options[:name] = get_app_name if @options[:name].nil?
        app_operation_req = app_operation __method__, @options[:name] if is_app_exist? @options[:name]
        exit! if app_operation_req
      end

      # app42 restart
      #
      # restart the app, return true or error code/message
      #
      def restart 
        @options[:name] = get_app_name if @options[:name].nil?
        app_operation_req = app_operation __method__, @options[:name] if is_app_exist? @options[:name]
        exit! if app_operation_req
      end

      # app42 delete
      #
      # delete the app, return true or error code/message
      #
      def delete
        @options[:name] = get_app_name if @options[:name].nil?
        app_operation_req = app_operation __method__, @options[:name] if is_app_exist? @options[:name]
        exit! if app_operation_req
      end

      # List deployed applications
      def apps
        response = build_get_request params, 'app', nil
        rows, rows_header_final, rows_header = [], [], nil
        unless response['apps'].nil? 
          response['apps'].each do |e|
            rows_header = e.keys 
            rows << e.values
          end
        else
          message "#{Message::NO_APP}", true, 'red'
          exit!  
        end   
        
        rows_header.map { |e| rows_header_final << camel_case_to_whitespace(e) }

        table = Terminal::Table.new  :title => Paint["=== My Apps ===", :green], :headings => rows_header_final, :rows => rows
        puts table
      end

      # Add custom URL to app 
      def add_custom_url
        @options[:name] = get_app_name if @options[:name].nil?
        custom_url = get_custom_url if is_app_exist? @options[:name]
        costom_url_res = costom_url_operation "addcustomurl", @options[:name], custom_url
        exit! if costom_url_res
      end

      # Remove custom URL of app
      def remove_custom_url
        @options[:name] = get_app_name if @options[:name].nil?
        custom_url = get_custom_url if is_app_exist? @options[:name]
        costom_url_res = costom_url_operation "removecustomurl", @options[:name], custom_url
        exit! if costom_url_res
      end

      # List all custom urls of apps
      def urls
        @options[:name] = get_app_name if @options[:name].nil?

        rows, rows_header_final, rows_header = [], [], nil
        custom_url_info = custom_url_information @options[:name]
        if custom_url_info && custom_url_info['urls']
          custom_url_info['urls'].each do |e|
            rows_header = e.keys 
            rows << e.values
          end

          rows_header.map { |e| rows_header_final << camel_case_to_whitespace(e) } 

          table = Terminal::Table.new  :title => Paint["=== #{@options[:name]} Custome URLs Details ===", :green], :headings => rows_header_final, :rows => rows
          puts table
        end

      end

    end
  end 
end