defmodule MvWeb.MemberLive.Index.MembershipFeeStatus do
  @moduledoc """
  Helper module for membership fee status display in member list view.

  Provides functions to efficiently load and determine cycle status for members
  in the list view, avoiding N+1 queries.
  """

  use Gettext, backend: MvWeb.Gettext

  alias Mv.Membership.Member
  alias MvWeb.Helpers.MembershipFeeHelpers

  @doc """
  Loads membership fee cycles for members efficiently.

  Preloads cycles with membership_fee_type relationship to avoid N+1 queries.
  Note: This loads all cycles for each member. The filtering to get the relevant
  cycle (current or last completed) happens in `get_cycle_status_for_member/2`.

  ## Parameters

    - `query` - Ash query for members
    - `show_current` - If true, get current cycle status; if false, get last completed cycle status (currently unused, kept for API compatibility)
    - `today` - Optional date to use as reference (currently unused, kept for API compatibility)

  ## Returns

    Modified query with cycles loaded

  ## Performance

    Uses Ash.Query.load to efficiently preload cycles in a single query.
    All cycles are loaded; filtering happens in memory in `get_cycle_status_for_member/2`.
  """
  @spec load_cycles_for_members(Ash.Query.t(), boolean(), Date.t() | nil) :: Ash.Query.t()
  def load_cycles_for_members(query, _show_current \\ false, _today \\ nil) do
    # Load membership_fee_type and cycles
    query
    |> Ash.Query.load([:membership_fee_type, membership_fee_cycles: [:membership_fee_type]])
  end

  @doc """
  Gets the cycle status for a member.

  Returns the status of either the last completed cycle or the current cycle,
  depending on the `show_current` parameter.

  ## Parameters

    - `member` - Member struct with loaded cycles and membership_fee_type
    - `show_current` - If true, get current cycle status; if false, get last completed cycle status

  ## Returns

    - `:paid`, `:unpaid`, or `:suspended` if cycle exists
    - `nil` if no cycle exists

  ## Examples

      # Get last completed cycle status
      iex> MvWeb.MemberLive.Index.MembershipFeeStatus.get_cycle_status_for_member(member, false)
      :paid

      # Get current cycle status
      iex> MvWeb.MemberLive.Index.MembershipFeeStatus.get_cycle_status_for_member(member, true)
      :unpaid
  """
  @spec get_cycle_status_for_member(Member.t(), boolean(), Date.t() | nil) ::
          :paid | :unpaid | :suspended | nil
  def get_cycle_status_for_member(member, show_current \\ false, today \\ nil) do
    cycle =
      if show_current do
        MembershipFeeHelpers.get_current_cycle(member, today)
      else
        MembershipFeeHelpers.get_last_completed_cycle(member, today)
      end

    case cycle do
      nil -> nil
      cycle -> cycle.status
    end
  end

  @doc """
  Formats cycle status as a badge component.

  Returns a map with badge information for rendering in templates.

  ## Parameters

    - `status` - Cycle status (`:paid`, `:unpaid`, `:suspended`, or `nil`)

  ## Returns

    Map with `:color`, `:icon`, and `:label` keys, or `nil` if status is nil

  ## Examples

      iex> MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(:paid)
      %{color: "badge-success", icon: "hero-check-circle", label: "Paid"}

      iex> MvWeb.MemberLive.Index.MembershipFeeStatus.format_cycle_status_badge(nil)
      nil
  """
  @spec format_cycle_status_badge(:paid | :unpaid | :suspended | nil) ::
          %{color: String.t(), icon: String.t(), label: String.t()} | nil
  def format_cycle_status_badge(nil), do: nil

  def format_cycle_status_badge(status) when status in [:paid, :unpaid, :suspended] do
    %{
      color: MembershipFeeHelpers.status_color(status),
      icon: MembershipFeeHelpers.status_icon(status),
      label: format_status_label(status)
    }
  end

  @doc """
  Filters members by cycle status (paid or unpaid).

  Returns members that have the specified status in either the last completed cycle
  or the current cycle, depending on `show_current`.

  ## Parameters

    - `members` - List of member structs with loaded cycles
    - `status` - Cycle status to filter by (`:paid` or `:unpaid`)
    - `show_current` - If true, filter by current cycle; if false, filter by last completed cycle

  ## Returns

    List of members with the specified cycle status

  ## Examples

      # Filter unpaid members in last cycle
      iex> filter_members_by_cycle_status(members, :unpaid, false)
      [%Member{}, ...]

      # Filter paid members in current cycle
      iex> filter_members_by_cycle_status(members, :paid, true)
      [%Member{}, ...]
  """
  @spec filter_members_by_cycle_status([Member.t()], :paid | :unpaid, boolean()) :: [Member.t()]
  def filter_members_by_cycle_status(members, status, show_current \\ false)
      when status in [:paid, :unpaid] do
    Enum.filter(members, fn member ->
      member_status = get_cycle_status_for_member(member, show_current)
      member_status == status
    end)
  end

  @doc """
  Filters members by unpaid cycle status.

  Returns members that have unpaid cycles in either the last completed cycle
  or the current cycle, depending on `show_current`.

  ## Parameters

    - `members` - List of member structs with loaded cycles
    - `show_current` - If true, filter by current cycle; if false, filter by last completed cycle

  ## Returns

    List of members with unpaid cycles

  ## Deprecated

    This function is kept for backwards compatibility. Use `filter_members_by_cycle_status/3` instead.
  """
  @spec filter_unpaid_members([Member.t()], boolean()) :: [Member.t()]
  def filter_unpaid_members(members, show_current \\ false) do
    filter_members_by_cycle_status(members, :unpaid, show_current)
  end

  # Private helper function to format status label
  defp format_status_label(:paid), do: gettext("Paid")
  defp format_status_label(:unpaid), do: gettext("Unpaid")
  defp format_status_label(:suspended), do: gettext("Suspended")
end
