class Api::V2::JobInvocationsController

def action_permission

def action_permission
  case params[:action]
  when 'output', 'raw_output', 'outputs', 'hosts'
    :view
  when 'cancel'
    :cancel
  when 'rerun'
    :create
  else
    super
  end
end

def allowed_nested_id

def allowed_nested_id
  %w(job_invocation_id)
end

def cancel

def cancel
  if @job_invocation.task.cancellable?
    result = @job_invocation.cancel(params.fetch('force', false))
    render :json => { :cancelled => result, :id => @job_invocation.id }
  else
    render :json => { :message => _('The job could not be cancelled.') },
      :status => :unprocessable_entity
  end
end

def create

def create
  composer = JobInvocationComposer.from_api_params(
    job_invocation_params
  )
  composer.trigger!
  @job_invocation = composer.job_invocation
  @hosts = @job_invocation.targeting.hosts
  process_response @job_invocation
rescue JobInvocationComposer::JobTemplateNotFound, JobInvocationComposer::FeatureNotFound => e
  not_found(error: { message: e.message })
end

def delayed_task_output(task, default: nil)

def delayed_task_output(task, default: nil)
  { :complete => false, :refresh => true, :output => default, :delayed => true, :start_at => task.start_at }
end

def find_host

def find_host
  @host = @nested_obj.targeting.hosts.authorized(:view_hosts, Host).find(params['host_id'])
rescue ActiveRecord::RecordNotFound
  not_found({ :error => { :message => (_("Host with id '%{id}' was not found") % { :id => params['host_id'] }) } })
end

def host_output(job_invocation, host, default: nil, since: nil, raw: false)

def host_output(job_invocation, host, default: nil, since: nil, raw: false)
  refresh = !job_invocation.finished?
  if (task = job_invocation.sub_task_for_host(host))
    refresh = task.pending?
    output  = output_lines_since(task, since)
    output  = output.map { |set| set['output'] }.join if raw
  end
  { :complete => !refresh, :refresh => refresh, :output => output || default }
end

def hosts

def hosts
  set_hosts_and_template_invocations
  set_statuses_and_smart_proxies
  @total = @job_invocation.targeting.hosts.size
  @hosts = @hosts.search_for(params[:search], :order => params[:order]).paginate(:page => params[:page], :per_page => params[:per_page])
  @subtotal = @hosts.respond_to?(:total_entries) ? @hosts.total_entries : @hosts.sizes
  render :hosts, :layout => 'api/v2/layouts/index_layout'
end

def index

def index
  @job_invocations = resource_scope_for_index
end

def job_invocation_params

def job_invocation_params
  return @job_invocation_params if @job_invocation_params.present?
  job_invocation_params = params.fetch(:job_invocation, {}).dup
  if job_invocation_params[:feature].present? && job_invocation_params[:job_template_id].present?
    raise _("Only one of feature or job_template_id can be specified")
  end
  if job_invocation_params.key?(:ssh)
    job_invocation_params.merge!(job_invocation_params.delete(:ssh).permit(:effective_user, :effective_user_password))
  end
  job_invocation_params[:inputs] ||= {}
  job_invocation_params[:inputs].permit!
  permit_provider_inputs job_invocation_params
  @job_invocation_params = job_invocation_params
end

def output

def output
  if @nested_obj.task.scheduled?
    render :json => delayed_task_output(@nested_obj.task, :default => [])
    return
  end
  render :json => host_output(@nested_obj, @host, :default => [], :since => params[:since])
end

def output_lines_since(task, time)

def output_lines_since(task, time)
  since = time.to_f if time.present?
  line_sets = task.main_action.live_output
  line_sets = line_sets.drop_while { |o| o['timestamp'].to_f <= since } if since
  line_sets
end

def outputs

def outputs
  hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
  hosts = hosts.search_for(params['search_query']) if params['search_query']
  raw = ActiveRecord::Type::Boolean.new.cast params['raw']
  default_value = raw ? '' : []
  outputs = hosts.map do |host|
    host_output(@job_invocation, host, :default => default_value, :since => params['since'], :raw => raw)
      .merge(host_id: host.id)
  end
  render :json => { :outputs => outputs }
end

def parent_scope

Do not try to scope JobInvocations by taxonomies
def parent_scope
  resource_class.where(nil)
end

def permit_provider_inputs(invocation_params)

def permit_provider_inputs(invocation_params)
  providers = RemoteExecutionProvider.providers.values.reject { |provider| !provider.respond_to?(:provider_input_namespace) || provider.provider_input_namespace.empty? }
  providers.each { |provider| invocation_params[provider.provider_input_namespace]&.permit! }
end

def raw_output

def raw_output
  if @nested_obj.task.scheduled?
    render :json => delayed_task_output(@nested_obj.task)
    return
  end
  render :json => host_output(@nested_obj, @host, :raw => true)
end

def rerun

def rerun
  composer = JobInvocationComposer.from_job_invocation(@job_invocation, params)
  if composer.rerun_possible?
    composer.trigger!
    @job_invocation = composer.job_invocation
    process_response @job_invocation
  else
    render :json => { :error => _('Could not rerun job %{id} because its template could not be found') % { :id => composer.reruns } },
      :status => :not_found
  end
end

def resource_name(resource = controller_name)

def resource_name(resource = controller_name)
  case resource
  when 'organization', 'location'
    nil
  else
    'job_invocation'
  end
end

def set_hosts_and_template_invocations

def set_hosts_and_template_invocations
  @pattern_template_invocations = @job_invocation.pattern_template_invocations.includes(:input_values)
  @hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)
  unless params[:search].nil?
    @hosts = @hosts.joins(:template_invocations)
                   .where(:template_invocations => { :job_invocation_id => @job_invocation.id})
  end
  @template_invocations = @job_invocation.template_invocations
                                         .where(host: @hosts)
                                         .includes(:input_values)
end

def set_statuses_and_smart_proxies

def set_statuses_and_smart_proxies
  template_invocations = @template_invocations.includes(:run_host_job_task).to_a
  hosts = @hosts.to_a
  @host_statuses = Hash[hosts.map do |host|
    template_invocation = template_invocations.find { |ti| ti.host_id == host.id }
    task = template_invocation.try(:run_host_job_task)
    [host.id, template_invocation_status(task, @job_invocation.task)]
  end]
  @smart_proxy_id = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_id] }]
  @smart_proxy_name = Hash[template_invocations.map { |ti| [ti.host_id, ti.smart_proxy_name] }]
end

def show

def show
  set_hosts_and_template_invocations
  @job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
  @job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
  if params[:host_status] == 'true'
    set_statuses_and_smart_proxies
  end
end