class RuboCop::Cop::Style::RedundantSort


arr.max_by(&:foo)
# good

arr.sort_by(&:foo).slice(-1)
arr.sort_by(&:foo).at(-1)
arr.sort_by(&:foo)[-1]
arr.sort_by(&:foo).last
# bad
arr.min_by(&:foo)
# good

arr.sort_by(&:foo).slice(0)
arr.sort_by(&:foo).at(0)
arr.sort_by(&:foo)[0]
arr.sort_by(&:foo).first
# bad
[2, 1, 3].max
# good
[2, 1, 3].sort.slice(-1)
[2, 1, 3].sort.at(-1)
[2, 1, 3].sort[-1]
[2, 1, 3].sort.last
# bad
[2, 1, 3].min
# good
[2, 1, 3].sort.slice(0)
[2, 1, 3].sort.at(0)
[2, 1, 3].sort[0]
[2, 1, 3].sort.first
# bad
@example
after which only the first or last element is used.
‘Enumerable#max_by` can replace `Enumerable#sort_by` calls
last element. Similarly, `Enumerable#min_by` and
element and `Enumerable#max` instead of sorting and taking the
`Enumerable#min` instead of sorting and taking the first
be accomplished without a relatively expensive sort by using
taking only the first or last element. The same behavior can
This cop is used to identify instances of sorting and then

def accessor_start(node)

(e.g. `.first`) or doesn't (e.g. `[0]`)
This gets the start of the accessor whether it has a dot
def accessor_start(node)
  if node.loc.dot
    node.loc.dot.begin_pos
  else
    node.loc.selector.begin_pos
  end
end

def arg_node(node)

def arg_node(node)
  node.arguments.first
end

def arg_value(node)

def arg_value(node)
  arg_node(node).nil? ? nil : arg_node(node).node_parts.first
end

def autocorrect(corrector, node, sort_node, sorter, accessor)

def autocorrect(corrector, node, sort_node, sorter, accessor)
  # Remove accessor, e.g. `first` or `[-1]`.
  corrector.remove(range_between(accessor_start(node), node.loc.expression.end_pos))
  # Replace "sort" or "sort_by" with the appropriate min/max method.
  corrector.replace(sort_node.loc.selector, suggestion(sorter, accessor, arg_value(node)))
end

def base(accessor, arg)

def base(accessor, arg)
  if accessor == :first || arg&.zero?
    'min'
  elsif accessor == :last || arg == -1
    'max'
  end
end

def message(node, sorter, accessor)

def message(node, sorter, accessor)
  accessor_source = range_between(
    node.loc.selector.begin_pos,
    node.loc.expression.end_pos
  ).source
  format(MSG,
         suggestion: suggestion(sorter,
                                accessor,
                                arg_value(node)),
         sorter: sorter,
         accessor_source: accessor_source)
end

def offense_range(sort_node, ancestor)

def offense_range(sort_node, ancestor)
  range_between(sort_node.loc.selector.begin_pos, ancestor.loc.expression.end_pos)
end

def on_send(node)

def on_send(node)
  if (sort_node, sorter, accessor = redundant_sort?(node.parent))
    ancestor = node.parent
  elsif (sort_node, sorter, accessor = redundant_sort?(node.parent&.parent))
    ancestor = node.parent.parent
  else
    return
  end
  message = message(ancestor, sorter, accessor)
  add_offense(offense_range(sort_node, ancestor), message: message) do |corrector|
    autocorrect(corrector, ancestor, sort_node, sorter, accessor)
  end
end

def suffix(sorter)

def suffix(sorter)
  case sorter
  when :sort
    ''
  when :sort_by
    '_by'
  end
end

def suggestion(sorter, accessor, arg)

def suggestion(sorter, accessor, arg)
  base(accessor, arg) + suffix(sorter)
end