defmodule Oban.Pro.Workers.Batch do
  @moduledoc """
  A dedicated worker for grouping the execution of jobs.

  This worker is deprecated in favor of `Oban.Pro.Batch`, which is more flexible and supports more
  options.
  """

  @moduledoc deprecated: "Use Oban.Pro.Batch instead"

  alias Oban.{Job, Worker}

  @callback new_batch([Job.t() | Job.args()], [keyword()]) :: Oban.Pro.Batch.t()

  @callback gen_id() :: String.t()

  @callback handle_attempted(Job.t()) :: Worker.result()

  @callback handle_cancelled(Job.t()) :: Worker.result()

  @callback handle_completed(Job.t()) :: Worker.result()

  @callback handle_discarded(Job.t()) :: Worker.result()

  @callback handle_exhausted(Job.t()) :: Worker.result()

  @callback handle_retryable(Job.t()) :: Worker.result()

  @callback all_batch_jobs(Job.t(), [keyword()]) :: Enum.t()

  @callback stream_batch_jobs(Job.t(), [keyword()]) :: Enum.t()

  @optional_callbacks handle_attempted: 1,
                      handle_cancelled: 1,
                      handle_completed: 1,
                      handle_discarded: 1,
                      handle_exhausted: 1,
                      handle_retryable: 1

  @doc false
  defmacro __using__(opts) do
    quote do
      use Oban.Pro.Worker, unquote(opts)

      alias Oban.Pro.Workers.Batch

      @behaviour Oban.Pro.Workers.Batch

      @batch_opts ~w(
        batch_id
        batch_name
        batch_callback_args
        batch_callback_meta
        batch_callback_queue
        batch_callback_worker
        callback_opts
        callback_worker
      )a

      @impl Batch
      def new_batch([_ | _] = args_or_changesets, opts \\ []) when is_list(opts) do
        {batch_opts, job_opts} = Keyword.split(opts, @batch_opts)

        batch_opts =
          Enum.reduce(batch_opts, [callback_opts: []], fn {key, val}, acc ->
            case key do
              :batch_callback_args -> put_in(acc, [:callback_opts, :args], val)
              :batch_callback_meta -> put_in(acc, [:callback_opts, :meta], val)
              :batch_callback_queue -> put_in(acc, [:callback_opts, :queue], val)
              :batch_callback_worker -> Keyword.put(acc, :callback_worker, val)
              _ -> Keyword.put(acc, key, val)
            end
          end)

        case args_or_changesets do
          [changeset | _] = changesets when is_struct(changeset) ->
            Oban.Pro.Batch.new(changesets, batch_opts)

          args ->
            args
            |> Enum.map(&new(&1, job_opts))
            |> Oban.Pro.Batch.new(batch_opts)
        end
      end

      @impl Batch
      def gen_id, do: Oban.Pro.UUIDv7.generate()

      @impl Batch
      def all_batch_jobs(job, opts \\ []), do: Oban.Pro.Batch.all_jobs(Oban, job, opts)

      @impl Batch
      def stream_batch_jobs(job, opts \\ []), do: Oban.Pro.Batch.stream_jobs(Oban, job, opts)

      defoverridable Oban.Pro.Workers.Batch
    end
  end
end
