class Avo::BaseAction

def action_name

def action_name
  if name.present?
    return Avo::ExecutionContext.new(
      target: name,
      resource: @resource,
      record: @record,
      view: @view,
      arguments: @arguments,
      query: @query
    ).handle
  end
  self.class.to_s.demodulize.underscore.humanize(keep_id_suffix: true)
end

def add_message(body, type = :info, timeout: nil)

def add_message(body, type = :info, timeout: nil)
  response[:messages] << {
    type: type,
    body: body&.truncate(320),
    timeout: timeout
  }
end

def append_to_response(turbo_stream)

def append_to_response(turbo_stream)
  @appended_turbo_streams = turbo_stream
end

def authorized?

def authorized?
  Avo::ExecutionContext.new(
    target: authorize,
    action: self,
    resource: @resource,
    view: @view,
    arguments: arguments
  ).handle
end

def cancel_button_label

def cancel_button_label
  Avo::ExecutionContext.new(
    target: self.class.cancel_button_label,
    resource: @resource,
    record: @record,
    view: @view,
    arguments: @arguments,
    query: @query
  ).handle
end

def close_modal

def close_modal
  response[:type] = :close_modal
  self
end

def confirm_button_label

def confirm_button_label
  Avo::ExecutionContext.new(
    target: self.class.confirm_button_label,
    resource: @resource,
    record: @record,
    view: @view,
    arguments: @arguments,
    query: @query
  ).handle
end

def current_user

def current_user
  Avo::Current.user
end

def decode_arguments(arguments)

def decode_arguments(arguments)
  return if arguments.blank?
  Avo::Services::EncryptionService.decrypt(
    message: Base64.decode64(arguments),
    purpose: :action_arguments
  )
end

def disabled?

def disabled?
  !enabled?
end

def download(path, filename)

def download(path, filename)
  response[:type] = :download
  response[:path] = path
  response[:filename] = filename
  self
end

def enabled?

def enabled?
  self.class.standalone || @record&.to_param.present?
end

def encode_arguments(arguments)

We use Base64 to encode the encrypted string so we can safely pass it as a query param and don't break the URL.
EncryptionService can generate special characters that can break the URL.
Encrypt the arguments so we can pass sensible data as a query param.
def encode_arguments(arguments)
  return if arguments.blank?
  Base64.encode64 Avo::Services::EncryptionService.encrypt(
    message: arguments,
    purpose: :action_arguments
  )
end

def error(text, timeout: nil)

def error(text, timeout: nil)
  add_message text, :error, timeout: timeout
  self
end

def fields

Blank method
def fields
end

def form_data_attributes

def form_data_attributes
  {
    turbo: turbo,
    turbo_frame: :_top
  }.compact
end

def get_message

def get_message
  Avo::ExecutionContext.new(
    target: self.class.message,
    resource: @resource,
    record: @record,
    view: @view,
    arguments: @arguments,
    query: @query
  ).handle
end

def handle_action(**args)

def handle_action(**args)
  processed_fields = if args[:fields].present?
    # Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method.
    action_fields = get_field_definitions.map do |field|
      field.hydrate(resource: @resource)
      [field.id, field]
    end.to_h
    # For some fields, like belongs_to, the id and database_id differ (user vs user_id).
    # That's why we need to fetch the database_id for when we process the action.
    action_fields_by_database_id = action_fields.map do |id, value|
      [value.database_id.to_sym, value]
    end.to_h
    args[:fields].to_unsafe_h.map do |name, value|
      field = action_fields_by_database_id[name.to_sym]
      next if field.blank?
      [name, field.resolve_attribute(value)]
    end.reject(&:blank?).to_h
  else
    {}
  end
  handle(
    fields: processed_fields.with_indifferent_access,
    current_user: args[:current_user],
    resource: args[:resource],
    records: args[:query],
    query: args[:query]
  )
  self
end

def inform(text, timeout: nil)

def inform(text, timeout: nil)
  add_message text, :info, timeout: timeout
  self
end

def initialize(record: nil, resource: nil, user: nil, view: nil, arguments: {}, icon: :play, query: nil, index_query: nil)

def initialize(record: nil, resource: nil, user: nil, view: nil, arguments: {}, icon: :play, query: nil, index_query: nil)
  @record = record
  @resource = resource
  @user = user
  @view = Avo::ViewInquirer.new(view)
  @icon = icon
  @arguments = Avo::ExecutionContext.new(
    target: arguments,
    resource: resource,
    record: record
  ).handle.with_indifferent_access
  @query = query
  @index_query = index_query
  self.class.message ||= I18n.t("avo.are_you_sure_you_want_to_run_this_option")
  self.class.confirm_button_label ||= I18n.t("avo.run")
  self.class.cancel_button_label ||= I18n.t("avo.cancel")
  self.items_holder = Avo::Resources::Items::Holder.new
  @response ||= {}
  @response[:messages] = []
  if may_download_file.present?
    puts "[Avo->] WARNING! Since version 3.2.2 'may_download_file' is unecessary and deprecated on actions. Can be safely removed from #{self.class.name}"
  end
end

def keep_modal_open

def keep_modal_open
  response[:type] = :keep_modal_open
  self
end

def link_arguments(resource:, arguments: {}, **args)

def link_arguments(resource:, arguments: {}, **args)
  [path(resource:, arguments:, **args), DATA_ATTRIBUTES]
end

def navigate_to_action(action, **kwargs)

def navigate_to_action(action, **kwargs)
  response[:type] = :navigate_to_action
  response[:action] = action
  response[:navigate_to_action_args] = kwargs
  self
end

def no_confirmation?

def no_confirmation?
  Avo::ExecutionContext.new(
    target: no_confirmation,
    action: self,
    resource: @resource,
    view: @view,
    arguments:
  ).handle
end

def path(resource:, arguments: {}, **args)

def path(resource:, arguments: {}, **args)
  Avo::Services::URIService.parse(resource.record&.to_param.present? ? resource.record_path : resource.records_path)
    .append_paths("actions")
    .append_query(
      **{
        action_id: to_param,
        arguments: encode_arguments(arguments),
        resource_view: resource.view,
        view_type: resource.view_type,
        **args
      }.compact
    )
    .to_s
end

def redirect_to(path = nil, **args, &block)

def redirect_to(path = nil, **args, &block)
  response[:type] = :redirect
  response[:redirect_args] = args
  response[:path] = if block.present?
    block
  else
    path
  end
  self
end

def reload

def reload
  response[:type] = :reload
  self
end

def reload_grid_items

def reload_grid_items
  append_to_response -> {
    component_to_replace = @resource.resolve_component(Avo::Index::GridItemComponent)
    @action.records_to_reload.map do |record|
      turbo_stream.replace(
        "#{component_to_replace.name.underscore}_#{record.to_param}",
        component_to_replace.new(resource: @resource.dup.hydrate(record:, view: :index), grid_item_checked: @action.records_to_reload.include?(record))
      )
    end
  }
end

def reload_record(records)

def reload_record(records)
  # Force close modal to avoid default redirect to
  # Redirect is 100% not wanted when using reload_record
  close_modal
  @records_to_reload = Array(records)
  case @resource.view_type.to_sym
  when :table, :map
    reload_row_items
  when :grid
    reload_grid_items
  end
end

def reload_row_items

def reload_row_items
  append_to_response -> {
    table_row_components = []
    header_fields = []
    component_to_replace = @resource.resolve_component(Avo::Index::TableRowComponent)
    @action.records_to_reload.each do |record|
      resource = @resource.dup
      resource.hydrate(record:, view: :index)
      resource.detect_fields
      row_fields = resource.get_fields(only_root: true)
      header_fields.concat row_fields
      table_row_components << component_to_replace.new(
        resource: resource,
        header_fields: row_fields.map(&:table_header_label),
        fields: row_fields,
        row_selector_checked: @action.records_to_reload.include?(record)
      )
    end
    header_fields.uniq!(&:table_header_label)
    header_fields_ids = header_fields.map(&:table_header_label)
    table_row_components.map.with_index do |table_row_component, index|
      table_row_component.header_fields = header_fields_ids
      turbo_stream.replace(
        "#{component_to_replace.name.underscore}_#{@action.records_to_reload[index].to_param}",
        table_row_component
      )
    end
  }
end

def silent

Add a placeholder silent message from when a user wants to do a redirect action or something similar
def silent
  add_message nil, :silent
  self
end

def succeed(text, timeout: nil)

def succeed(text, timeout: nil)
  add_message text, :success, timeout: timeout
  self
end

def to_param

def to_param
  to_s
end

def visible_in_view(parent_resource: nil)

def visible_in_view(parent_resource: nil)
  Avo::ExecutionContext.new(
    target: visible,
    params: params,
    parent_resource: parent_resource,
    resource: @resource,
    view: @view,
    arguments: arguments
  ).handle && authorized?
end

def warn(text, timeout: nil)

def warn(text, timeout: nil)
  add_message text, :warning, timeout: timeout
  self
end