class InputTemplateRenderer
  class UndefinedInput < ::Foreman::Exception
  end

  class RenderError < ::Foreman::Exception
  end

  delegate :logger, to: Rails

  attr_accessor :template, :host, :invocation, :input_values, :error_message, :templates_stack

  # takes template object that should be rendered
  # host and template invocation arguments are optional
  # so we can render values based on parameters, facts or user inputs
  def initialize(template, host = nil, invocation = nil, input_values = nil, preview = false, templates_stack = [])
    raise Foreman::Exception, N_('Recursive rendering of templates detected') if templates_stack.include?(template)

    @host = host
    @template = template
    @invocation = invocation
    @input_values = input_values
    @preview = preview
    @templates_stack = templates_stack + [template]
  end

  def render
    @template.validate_unique_inputs!
    source = Foreman::Renderer.get_source(
      template: template,
      host: host
    )
    scope = Foreman::Renderer.get_scope(
      host: host,
      klass: renderer_scope,
      variables: {
        host: host,
        template: template,
        preview: @preview,
        invocation: invocation,
        input_values: input_values,
        templates_stack: templates_stack,
        input_template_instance: self
      }
    )
    Foreman::Renderer.render(source, scope)
  rescue => e
    self.error_message ||= _('error during rendering: %s') % e.message
    Foreman::Logging.exception('Error during rendering of a job template', e)
    false
  end

  def input(name)
    return @input_values[name.to_s] if @input_values
    input = find_by_name(template.template_inputs_with_foreign, name) # rubocop:disable Rails/DynamicFindBy
    if input
      @preview ? input.preview(self) : input.value(self)
    else
      error_message = _('input macro with name \'%s\' used, but no input with such name defined for this template') % name
      raise UndefinedInput, "Rendering failed, no input with name #{name} for input macro found"
    end
  end

  def preview
    old_preview = @preview
    @preview = true
    render
  ensure
    @preview = old_preview
  end

  private

  def renderer_scope
    ForemanRemoteExecution::Renderer::Scope::Input
  end

  def find_by_name(collection, name)
    collection.detect { |i| i.name == name.to_s }
  end
end