defmodule Mv.Membership.MemberExportSort do
  @moduledoc """
  Type-stable sort keys for CSV export custom-field sorting.

  Used only by `MvWeb.MemberExportController` when sorting members by a custom field
  after load. Nil values sort last in ascending order and first in descending order.
  String and email comparison is case-insensitive.
  """
  @doc """
  Returns a comparable sort key for (value_type, value).

  - Nil: rank 1 so that in asc order nil sorts last, in desc nil sorts first.
  - date: chronological (ISO8601 string).
  - boolean: false < true (0 < 1).
  - integer: numerical order.
  - string / email: case-insensitive (downcased).

  Handles Ash.Union in value; value_type is the custom field's value_type atom.
  """
  @spec custom_field_sort_key(:string | :integer | :boolean | :date | :email, term()) ::
          {0 | 1, term()}
  def custom_field_sort_key(_value_type, nil), do: {1, nil}

  def custom_field_sort_key(value_type, %Ash.Union{value: value, type: _type}) do
    custom_field_sort_key(value_type, value)
  end

  def custom_field_sort_key(:date, %Date{} = d), do: {0, Date.to_iso8601(d)}
  def custom_field_sort_key(:boolean, true), do: {0, 1}
  def custom_field_sort_key(:boolean, false), do: {0, 0}
  def custom_field_sort_key(:integer, v) when is_integer(v), do: {0, v}
  def custom_field_sort_key(:string, v) when is_binary(v), do: {0, String.downcase(v)}
  def custom_field_sort_key(:email, v) when is_binary(v), do: {0, String.downcase(v)}
  def custom_field_sort_key(_value_type, v), do: {0, to_string(v)}

  @doc """
  Returns true if key_a should sort before key_b for the given order.

  "asc" -> nil last; "desc" -> nil first. No reverse of list needed.
  """
  @spec key_lt({0 | 1, term()}, {0 | 1, term()}, String.t()) :: boolean()
  def key_lt(key_a, key_b, "asc"), do: key_a < key_b
  def key_lt(key_a, key_b, "desc"), do: key_b < key_a
end
