defmodule Mv.Membership.Member.Changes.UnrelateUserWhenArgumentNil do
  @moduledoc """
  When :user argument is present and nil/empty on update_member, unrelate the current user.

  With on_missing: :ignore, manage_relationship does not unrelate when input is nil/[].
  This change handles explicit unlink (user: nil or user: %{}) by updating the linked
  User to set member_id = nil. Only runs when the argument key is present (policy
  ForbidMemberUserLinkUnlessAdmin ensures only admins can pass :user).
  """
  use Ash.Resource.Change

  @spec change(Ash.Changeset.t(), keyword(), Ash.Resource.Change.context()) :: Ash.Changeset.t()
  def change(changeset, _opts, _context) do
    if unlink_requested?(changeset) do
      unrelate_current_user(changeset)
    else
      changeset
    end
  end

  defp unlink_requested?(changeset) do
    args = changeset.arguments || %{}

    if Map.has_key?(args, :user) or Map.has_key?(args, "user") do
      user_arg = Ash.Changeset.get_argument(changeset, :user)
      user_arg == nil or (is_map(user_arg) and map_size(user_arg) == 0)
    else
      false
    end
  end

  defp unrelate_current_user(changeset) do
    member = changeset.data
    actor = Map.get(changeset.context || %{}, :actor)

    case Ash.load(member, :user, domain: Mv.Membership, authorize?: false) do
      {:ok, %{user: user}} when not is_nil(user) ->
        # User's :update action only accepts [:email]; use :update_user so
        # manage_relationship(:member, ..., on_missing: :unrelate) runs and clears member_id.
        user
        |> Ash.Changeset.for_update(:update_user, %{member: nil}, domain: Mv.Accounts)
        |> Ash.update(domain: Mv.Accounts, actor: actor, authorize?: false)

        changeset

      _ ->
        changeset
    end
  end
end
