lib/foundry/context/pending_migrations.ex

defmodule Foundry.Context.PendingMigrations do
  @moduledoc """
  Determines which modules have pending Ash migrations by running
  `mix ash.codegen --check` once per project invocation.

  Do not call check/1 per module — it spawns one Mix process per call.
  Call once, then use pending?/2 to query individual modules.
  """

  @spec check(project_root :: String.t()) :: {:ok, MapSet.t()} | {:error, term()}
  def check(project_root) do
    case System.cmd("mix", ["ash.codegen", "--check"],
           cd: project_root, stderr_to_stdout: true) do
      {_output, 0} ->
        {:ok, MapSet.new()}

      {output, _nonzero} ->
        {:ok, MapSet.new(parse_pending_modules(output))}
    end
  rescue
    _ -> {:ok, MapSet.new()}
  end

  @spec pending?(module :: module(), pending_set :: MapSet.t()) :: boolean()
  def pending?(module, pending_set), do: MapSet.member?(pending_set, module)

  defp parse_pending_modules(output) do
    # ash.codegen --check output format is implementation-specific.
    # Adjust the regex to match the actual output of the ash_postgres version in use.
    Regex.scan(~r/\b([\w.]+)\b.*?pending migration/, output)
    |> Enum.flat_map(fn [_, mod] ->
      try do [String.to_existing_atom(mod)]
      rescue _ -> []
      end
    end)
  end
end