defmodule Mv.Vereinfacht.Changes.SyncLinkedMemberAfterUserChange do
  @moduledoc """
  Syncs the linked Member to Vereinfacht after a User action that may have updated
  the member's email via Ecto (e.g. User email change → SyncUserEmailToMember).

  Attach to any User action that uses SyncUserEmailToMember. After the transaction
  commits, if the user has a linked member and Vereinfacht is configured, syncs
  that member to the API. Failures are logged but do not affect the User result.
  """
  use Ash.Resource.Change

  require Logger
  alias Mv.Membership.Member
  alias Mv.Membership
  alias Mv.Helpers.SystemActor
  alias Mv.Helpers

  @impl true
  def change(changeset, _opts, _context) do
    if Mv.Config.vereinfacht_configured?() and relevant_change?(changeset) do
      Ash.Changeset.after_transaction(changeset, &sync_linked_member_after_transaction/2)
    else
      changeset
    end
  end

  # Only sync when something that affects the linked member's data actually changed
  # (email sync or member link), to avoid unnecessary API calls on every user update.
  defp relevant_change?(changeset) do
    Ash.Changeset.changing_attribute?(changeset, :email) or
      Ash.Changeset.changing_relationship?(changeset, :member)
  end

  defp sync_linked_member_after_transaction(_changeset, {:ok, user}) do
    case load_linked_member(user) do
      nil ->
        {:ok, user}

      member ->
        case Mv.Vereinfacht.sync_member(member) do
          :ok ->
            {:ok, user}

          {:ok, _} ->
            {:ok, user}

          {:error, reason} ->
            Logger.warning(
              "Vereinfacht sync failed for member #{member.id} (linked to user #{user.id}): #{inspect(reason)}"
            )

            {:ok, user}
        end
    end
  end

  defp sync_linked_member_after_transaction(_changeset, result), do: result

  defp load_linked_member(%{member_id: nil}), do: nil
  defp load_linked_member(%{member_id: ""}), do: nil

  defp load_linked_member(user) do
    actor = SystemActor.get_system_actor()
    opts = Helpers.ash_actor_opts(actor)

    case Ash.get(Member, user.member_id, [domain: Membership] ++ opts) do
      {:ok, %Member{} = member} -> member
      _ -> nil
    end
  end
end
