require 'lib/helpers/job_waiter'
require 'workers/bootstrap_worker'

# Starts bootstrap workers for each server in group and wait for them to end (synchroniously).
class Devops::Executor::StackExecutor
  class ServersBootstrapper
    include PutsAndFlush
    attr_reader :out

    class Result < Devops::Helpers::ResultObject
      set_result_codes(
        ok: 0,
        bootstrap_error: 2,
        deploy_error: 3,
        timeout_reached: 4
      )
    end

    def initialize(out, jid, servers)
      @out, @jid, @servers = out, jid, servers
      @server_bootstrap_jobs = {}
      @parent_task = Devops::Model::JobTask.find(jid)
    end

    # returns array of Results
    def bootstrap_group
      start_workers!
      @parent_task.add_subtasks(@jid, @server_bootstrap_jobs.values)

      @server_bootstrap_jobs.map do |server_id, job_id|
        result = wait_for_bootstrap_job(job_id)
        puts_and_flush Devops::Messages.t("stack_executor.servers_bootstrapper.result.#{result.reason}", server_id: server_id, job_id: job_id)
        result
      end
    end

    private

    # returns hash: {server_id => worker_job_id}
    def start_workers!
      @servers.each do |server|
        job_id = Worker.start_async(::BootstrapWorker,
          server_id: server.id,
          bootstrap_template: 'omnibus',
          owner: server.created_by,
          skip_rollback: true
        )
        @out.puts "Start bootstraping server '#{server.id}' job (job id: #{job_id})."
        @server_bootstrap_jobs[server.id] = job_id
      end
      puts_and_flush "\n\n\n"
    end

    def result(reason)
      Result.from_reason(reason)
    end

    def wait_for_bootstrap_job(job_id)
      result_code = Devops::Helpers::JobWaiter.new(job_id).wait
      result_from_job_code(result_code)
    rescue Devops::Helpers::JobWaiter::TimeoutReached
      result(:timeout_reached)
    end

    def result_from_job_code(result_code)
      job_result = Devops::Executor::ServerOperationResult.new(result_code)
      if job_result.ok?
        result(:ok)
      elsif job_result.one_of_bootstrap_errors?
        result(:bootstrap_error)
      else
        result(:deploy_error)
      end
    end
  end
end
