require "devops-client/handler/handler"
require "devops-client/handler/helpers/validators"
require "devops-client/options/category_options"
require "set"
require "devops-client/output/category"

class Category < Handler

  output_with Output::Category

  def initialize(host, def_options={})
    @host = host
    @options = def_options
  end

  def handle
    @options_parser = CategoryOptions.new(ARGV, @options)
    @command, @options, @args = @options_parser.parse_options_for!
    extract_parameters
    case @command
    when :create
      create_handler
    when :delete
      delete_handler
    when :deploy
      deploy_handler
    when :list
      list_handler
      output(project: @project_id, env: @environment)
    when :servers_list
      servers_handler
      output(project: @project_id, env: @environment)
    when :stacks
      stacks_handler
      output
    when :set_run_list
      set_run_list_handler
    when :show
      show_handler
      output(project: @project_id, env: @environment, cat: @category)
    when :update
      update_handler
    when :servers_delete
      delete_servers_handler
    end
  end

  def list_handler
    @list = get "/project/#{@project_id}/environment/#{@environment}/categories"
  end

  def delete_handler
    delete "/project/#{@project_id}/environment/#{@environment}/category/#{@category}" if question(I18n.t("handler.category.question.delete", env: @environment, cat: @category, project: @project_id))
  end

  def show_handler
    @show = get("/project/#{@project_id}/environment/#{@environment}/category/#{@category}")
  end

  def update_handler args
    r = inspect_parameters @options_parser.update_params, args[2], args[3]
    unless r.nil?
      @options_parser.invalid_update_command
      abort(r)
    end
    update_object_from_file "project", args[2], args[3]
  end

  def create_handler
    file = self.options[:file]
    json = nil
    unless file.nil?
      json = File.read(file)
      begin
        JSON.parse(json)
      rescue JSON::ParserError => e
        abort(I18n.t("handler.category.create.invalid_json", :file => file))
      end
    else
      cat = create_category
      json = JSON.pretty_generate(cat)
    end
    post_body("/project/#{@project_id}/environment/#{@environment}/category", json) if question(I18n.t("handler.category.question.create")){puts json}
  end

  def servers_handler
    @list = get "/project/#{@project_id}/environment/#{@environment}/servers"
  end

  def stacks_handler
    @list = get "/project/#{@project_id}/environment/#{@environment}/stacks"
  end

  def set_run_list_handler
    run_list = @run_list || []
    if run_list.empty?
      exit unless question(I18n.t("handler.project.question.run_list.empty"))
    else
      exit unless Validators.validate_run_list(run_list)
    end

    post "/project/#{@project_id}/environment/#{@environment}/run_list", run_list
  end

  def deploy_handler args
    q = {}
    q[:servers] = options[:servers] unless options[:servers].nil?
    job_ids = post "/project/#{args[2]}/deploy", q
    reports_urls(job_ids)
  end

  def test_handler args
    r = inspect_parameters @options_parser.test_params, args[2], args[3]
    unless r.nil?
      @options_parser.invalid_test_command
      abort(r)
    end
    job_ids = post "/project/test/#{args[2]}/#{args[3]}"
    reports_urls(job_ids)
  end

protected
  def get_project_info_obj project_id
    get("/project/#{project_id}")
  end

  def create_category
    cat = {id: @category, provider: {}, cm_tool: {}}

    case (self.options[:provider] || resources_selector.select_available_provider)
    when 'static'
      create_static_category_provider(cat[:provider])
    when 'aws'
      create_aws_category_provider(cat[:provider])
    end
    fill_configuration_manager_tool(cat[:cm_tool])
    cat
  end

  def create_static_category_provider cat_provider
    fill_category_provider("static", cat_provider)
  end

  def create_aws_category_provider cat_provider
    fill_category_provider("aws", cat_provider)
    cat_provider[:type] = choose_item_from_list(I18n.t('headers.category_type'), %w(server stack))
    if cat_provider[:type] == 'server'
      fill_category_cloud_provider cat_provider do
        cat_provider[:vpc_id] = self.options[:vpc_id] || resources_selector.select_available_vpc(provider: cat_provider[:name], account: cat_provider[:account])
        cat_provider[:subnet] = self.options[:subnet] || resources_selector.select_available_network(provider: cat_provider[:name], account: cat_provider[:account], vpc_id: cat_provider[:vpc_id])
      end
    else
      cat_provider[:stack_template] = self.options[:stack_template] || resources_selector.select_available_stack_template(provider: 'aws')
    end
  end

  def fill_category_provider provider_name, cat_provider
    cat_provider[:name] = provider_name
    cat_provider[:account] = self.options[:account] || resources_selector.select_available_provider_account(cat_provider[:name])["account_name"]
  end

  def fill_category_cloud_provider cat_provider
    cat_provider[:flavor] = self.options[:flavor] || resources_selector.select_available_flavor(provider: cat_provider[:name], account: cat_provider[:account])
    cat_provider[:image] = self.options[:image] || resources_selector.select_available_image(provider: cat_provider[:name], account: cat_provider[:account])
    yield
    cat_provider[:security_groups] = self.options[:security_groups] || resources_selector.select_available_security_groups(provider: cat_provider[:name], account: cat_provider[:account], vpc_id: cat_provider[:vpc_id])
#TODO    cat_provider[:stack_template] = self.options[:stack_template] || resources_selector.select_available_stack_template(provider: cat_provider[:name], account: cat_provider[:account])
  end

  def set_identifier d, names
    set_parameter d, :id do
      begin
        n = enter_parameter I18n.t("handler.environment.create.id") + ": "
        if names.include?(n)
          puts I18n.t("handler.environment.create.id_exists", :env => n)
          raise ArgumentError
        else
          names.push n
          n
        end
      rescue ArgumentError
        retry
      end
    end
  end

  def set_parameter obj, key
    if self.options[key].nil?
      obj[key] = yield
    else
      obj[key] = self.options[key]
    end
  end

  def data_to_output
    @list || @show || @test
  end

  def delete_servers_handler
    ask_for_delete_servers(@project_id, @environment)
    body = {
      dry_run: false
    }
    response = post("/project/#{@project_id}/environment/#{@environment}/servers/delete", body)
    reports_urls(response['reports'])
  end

  def fill_configuration_manager_tool tool
    tool[:name] = "chef"
    tool[:bootstrap_template] = self.options[:bootstrap_template] || resources_selector.select_available_bootstrap_template
    tool[:chef_env] = enter_parameter(I18n.t('handler.category.create.chef_env'))
  end
  private

  def ask_for_delete_servers(project, env)
    body = {
      dry_run: true
    }
    to_delete = post("/project/#{project}/environment/#{env}/servers/delete", body)
    if to_delete.empty?
      abort "There are no servers to delete."
    else
      puts "Servers to delete:\n----\n"
      puts to_delete.join("\n")
      puts '----'
    end
    if @options[:dry_run] || !question('Are you sure to delete them')
      abort
    end
  end

end
