defmodule Mv.EmailSync.Loader do
  @moduledoc """
  Helper functions for loading linked records in email synchronization.
  Centralizes the logic for retrieving related User/Member entities.

  ## Authorization-independent link checks

  All functions use the **system actor** for the load. Link existence
  (linked vs not linked) is therefore determined **independently of the
  current request actor**. This is required so that validations (e.g.
  `EmailChangePermission`, `EmailNotUsedByOtherUser`) can correctly decide
  "member is linked" even when the current user would not have read permission
  on the related User. Using the request actor would otherwise allow
  treating a linked member as unlinked and bypass the permission rule.
  """
  alias Mv.Helpers
  alias Mv.Helpers.SystemActor

  @doc """
  Loads the member linked to a user, returns nil if not linked or on error.

  Uses system actor for authorization to ensure email sync always works.
  """
  def get_linked_member(user)
  def get_linked_member(%{member_id: nil}), do: nil

  def get_linked_member(%{member_id: id}) do
    system_actor = SystemActor.get_system_actor()
    opts = Helpers.ash_actor_opts(system_actor)

    case Ash.get(Mv.Membership.Member, id, opts) do
      {:ok, member} -> member
      {:error, _} -> nil
    end
  end

  @doc """
  Loads the user linked to a member, returns nil if not linked or on error.

  Uses system actor for authorization to ensure email sync always works.
  """
  def get_linked_user(member) do
    system_actor = SystemActor.get_system_actor()
    opts = Helpers.ash_actor_opts(system_actor)

    case Ash.load(member, :user, opts) do
      {:ok, %{user: user}} -> user
      {:error, _} -> nil
    end
  end

  @doc """
  Loads the user linked to a member, returning an error tuple if not linked.
  Useful when a link is required for the operation.

  Uses system actor for authorization to ensure email sync always works.
  """
  def load_linked_user!(member) do
    system_actor = SystemActor.get_system_actor()
    opts = Helpers.ash_actor_opts(system_actor)

    case Ash.load(member, :user, opts) do
      {:ok, %{user: user}} when not is_nil(user) -> {:ok, user}
      {:ok, _} -> {:error, :no_linked_user}
      {:error, _} = error -> error
    end
  end
end
