require "devops-client/handler/handler"
require "devops-client/handler/helpers/validators"
require "devops-client/options/environment_options"
require "set"
require "devops-client/output/environment"

class Environment < Handler

  output_with Output::Environment

  def initialize(host, def_options={})
    @host = host
    @options = def_options
    @options_parser = EnvironmentOptions.new(ARGV, def_options)
  end

  def handle
    @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)
    when :servers_list
      servers_handler
      output
    when :stacks
      stacks_handler
      output
    when :set_run_list
      set_run_list_handler
    when :show
      show_handler
      output(project: @project_id, env: @environment)
    when :update
      update_handler
    when :user_add
      user_add_handler
    when :user_delete
      user_delete_handler
    when :servers_delete
      delete_servers_handler
    end
  end

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

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

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

  def update_handler args
    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.environment.create.invalid_json", :file => file))
      end
    else
      json = JSON.pretty_generate(create_environment)
    end
    post_body("/project/#{@project_id}/environment", json) if question(I18n.t("handler.environment.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 user_add_handler
    project = get_project_info_obj(@project_id)
    users = environment_users(project)
    post "/project/#{@project_id}/environment/#{@environment}/users/add", users
  end

  def user_delete_handler
    post "/project/#{@project_id}/environment/#{@environment}/users/delete", @users
  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
    r = inspect_parameters @options_parser.deploy_params, args[2], args[3]
    unless r.nil?
      @options_parser.invalid_deploy_command
      abort(r)
    end
    q = {}
    q[:servers] = options[:servers] unless options[:servers].nil?
    q[:deploy_env] = args[3] unless args[3].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_environment
    project = get_project_info_obj(@project_id)
    names = project["environments"].map{|env| env["id"]}
    abort(I18n.t("handler.environment.create.id_exists")) if names.include?(@environment)
    e = {categories: [], id: @environment}
#    set_identifier(e, names)

    e[:run_list] = if self.options[:run_list]
      abort("Invalid run list: '#{self.options[:run_list].join(",")}'") unless Validators.validate_run_list(self.options[:run_list])
      self.options[:run_list]
    else
      input_run_list
    end

    e[:users] = environment_users(project)

    unless self.options[:no_expires]
      set_parameter e, :expires do
        s = enter_parameter_or_empty(I18n.t("options.project.create.expires") + ": ").strip
        s.empty? ? nil : s
      end
    end
    e
  end

  def environment_users project
    if self.options[:users]
      self.options[:users]
    else
      if question(I18n.t("handler.project.create.question.users_choose"))
        #choose
        resources_selector.select_available_users(label: I18n.t("handler.project.create.users"), data: project["project_users"] + [project["owner"]])
      else
        #enter
        get_comma_separated_list(I18n.t("input.message.input_list", cmd: I18n.t("input.user.list")))
      end
    end
  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

  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
