defmodule Exq.Api do
  @moduledoc """
  Interface for retrieving Exq stats.

  Pid is currently Exq.Api process.
  """

  def start_link(opts \\ []) do
    Exq.start_link(Keyword.put(opts, :mode, :api))
  end

  @doc """
  List of queues with jobs (empty queues are deleted).

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, queues}` - list of queue

  """
  def queues(pid) do
    GenServer.call(pid, :queues)
  end

  @doc """
  Clear / Remove queue

  Expected args:
    * `pid` - Exq.Api process
    * `queue` - Queue name

  Returns:
    * `{:ok, queues}` - list of queue

  """
  def remove_queue(pid, queue) do
    GenServer.call(pid, {:remove_queue, queue})
  end

  @doc """
  Number of busy workers

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, num_busy}` - number of busy workers

  """
  def busy(pid) do
    GenServer.call(pid, :busy)
  end

  @doc """
  List of worker nodes currently running
  """

  def nodes(pid) do
    GenServer.call(pid, :nodes)
  end

  @doc """
  List of processes currently running

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, [processes]}`

  """
  def processes(pid) do
    GenServer.call(pid, :processes)
  end

  def clear_processes(pid) do
    GenServer.call(pid, :clear_processes)
  end

  @doc """
  List jobs enqueued

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, [{queue, [jobs]}, {queue, [jobs]}]}`

  """
  def jobs(pid) do
    GenServer.call(pid, :jobs)
  end

  @doc """
  List jobs enqueued

  Expected args:
    * `pid` - Exq.Api process
    * `queue` - Queue name
    * `options`
      - size: (integer) size of list
      - offset: (integer) start offset of the list
      - raw: (boolean) whether to deserialize the job

  Returns:
    * `{:ok, [jobs]}`

  """
  def jobs(pid, queue, options \\ []) do
    GenServer.call(pid, {:jobs, queue, options})
  end

  @doc """
  List jobs that will be retried because they previously failed and have not exceeded their retry_count.

  Expected args:
    * `pid` - Exq.Api process
    * `options`
      - score: (boolean) whether to include job score
      - size: (integer) size of list
      - offset: (integer) start offset of the list

  Returns:
    * `{:ok, [jobs]}`

  """
  def retries(pid, options \\ []) do
    GenServer.call(pid, {:retries, options})
  end

  @doc """
  List jobs that are enqueued and scheduled to be run at a future time.

  Expected args:
    * `pid` - Exq.Api process
    * `options`
      - score: (boolean) whether to include job score
      - size: (integer) size of list
      - offset: (integer) start offset of the list

  Returns:
    * `{:ok, [jobs]}`

  """
  def scheduled(pid, options \\ []) do
    GenServer.call(pid, {:jobs, :scheduled, options})
  end

  @doc """
  List jobs that are enqueued and scheduled to be run at a future time, along with when they are scheduled to run.

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, [{job, scheduled_at}]}`

  """
  def scheduled_with_scores(pid) do
    GenServer.call(pid, {:jobs, :scheduled_with_scores})
  end

  def find_job(pid, queue, jid) do
    GenServer.call(pid, {:find_job, queue, jid})
  end

  @doc """
  Removes a job from the queue specified.

  Expected args:
    * `pid` - Exq.Api process
    * `queue` - The name of the queue to remove the job from
    * `jid` - Unique identifier for the job

  Returns:
    * `:ok`

  """
  @deprecated "use remove_enqueued_jobs/3"
  def remove_job(pid, queue, jid) do
    GenServer.call(pid, {:remove_job, queue, jid})
  end

  @doc """
  Removes a job from the queue specified.

  Expected args:
    * `pid` - Exq.Api process
    * `queue` - The name of the queue to remove the job from
    * `raw_jobs` - list of raw json encoded job values
    * `options`
      - clear_unique_tokens: (boolean) whether to clear jobs unique tokens, default is `false`

  Returns:
    * `:ok`

  """
  def remove_enqueued_jobs(pid, queue, raw_jobs, options \\ []) do
    GenServer.call(pid, {:remove_enqueued_jobs, queue, raw_jobs, options})
  end

  @doc """
  A count of the number of jobs in the queue, for each queue.

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, [{queue, num_jobs}, {queue, num_jobs}]}`

  """
  def queue_size(pid) do
    GenServer.call(pid, :queue_size)
  end

  @doc """
  A count of the number of jobs in the queue, for a provided queue.

  Expected args:
    * `pid` - Exq.Api process
    * `queue` - The name of the queue to find the number of jobs for

  Returns:
    * `{:ok, num_jobs}`

  """
  def queue_size(pid, queue) do
    GenServer.call(pid, {:queue_size, queue})
  end

  @doc """
  List jobs that have failed and will not retry, as they've exceeded their retry count.

  Expected args:
    * `pid` - Exq.Api process
    * `options`
      - score: (boolean) whether to include job score
      - size: (integer) size of list
      - offset: (integer) start offset of the list

  Returns:
    * `{:ok, [jobs]}`

  """
  def failed(pid, options \\ []) do
    GenServer.call(pid, {:failed, options})
  end

  @deprecated "use find_failed/4"
  def find_failed(pid, jid) do
    GenServer.call(pid, {:find_failed, jid})
  end

  @doc """
  Find failed job

  Expected args:
    * `pid` - Exq.Api process
    * `score` - Job score
    * `jid` - Job jid
    * `options`
      - raw: (boolean) whether to deserialize the job

  Returns:
    * `{:ok, job}`

  """
  def find_failed(pid, score, jid, options \\ []) do
    GenServer.call(pid, {:find_failed, score, jid, options})
  end

  @doc """
  Removes a job in the queue of jobs that have failed and exceeded their retry count.

  Expected args:
    * `pid` - Exq.Api process
    * `jid` - Unique identifier for the job

  Returns:
    * `:ok`

  """
  @deprecated "use remove_failed_jobs/2"
  def remove_failed(pid, jid) do
    GenServer.call(pid, {:remove_failed, jid})
  end

  @doc """
  Removes jobs from dead queue.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values
    * `options`
      - clear_unique_tokens: (boolean) whether to clear jobs unique tokens, default is `false`

  Returns:
    * `:ok`

  """
  def remove_failed_jobs(pid, raw_jobs, options \\ []) do
    GenServer.call(pid, {:remove_failed_jobs, raw_jobs, options})
  end

  def clear_failed(pid) do
    GenServer.call(pid, :clear_failed)
  end

  @doc """
  Re Enqueue jobs from dead queue.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values

  Returns:
    * `{:ok, num_enqueued}`

  """
  def dequeue_failed_jobs(pid, raw_jobs) do
    GenServer.call(pid, {:dequeue_failed_jobs, raw_jobs})
  end

  @doc """
  Number of jobs that have failed and exceeded their retry count.

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, num_failed}` - number of failed jobs

  """
  def failed_size(pid) do
    GenServer.call(pid, :failed_size)
  end

  @deprecated "use find_retry/4"
  def find_retry(pid, jid) do
    GenServer.call(pid, {:find_retry, jid})
  end

  @doc """
  Find job in retry queue

  Expected args:
    * `pid` - Exq.Api process
    * `score` - Job score
    * `jid` - Job jid
    * `options`
      - raw: (boolean) whether to deserialize the job

  Returns:
    * `{:ok, job}`

  """
  def find_retry(pid, score, jid, options \\ []) do
    GenServer.call(pid, {:find_retry, score, jid, options})
  end

  @doc """
  Removes a job in the retry queue from being enqueued again.

  Expected args:
    * `pid` - Exq.Api process
    * `jid` - Unique identifier for the job

  Returns:
    * `:ok`

  """
  @deprecated "use remove_retry_jobs/2"
  def remove_retry(pid, jid) do
    GenServer.call(pid, {:remove_retry, jid})
  end

  @doc """
  Removes jobs from retry queue.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values
    * `options`
      - clear_unique_tokens: (boolean) whether to clear jobs unique tokens, default is `false`

  Returns:
    * `:ok`

  """
  def remove_retry_jobs(pid, raw_jobs, options \\ []) do
    GenServer.call(pid, {:remove_retry_jobs, raw_jobs, options})
  end

  def clear_retries(pid) do
    GenServer.call(pid, :clear_retries)
  end

  @doc """
  Re enqueue jobs from retry queue immediately.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values

  Returns:
    * `{:ok, num_enqueued}`

  """
  def dequeue_retry_jobs(pid, raw_jobs) do
    GenServer.call(pid, {:dequeue_retry_jobs, raw_jobs})
  end

  @doc """
  Number of jobs in the retry queue.

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, num_retry}` - number of jobs to be retried

  """
  def retry_size(pid) do
    GenServer.call(pid, :retry_size)
  end

  @deprecated "use find_scheduled/4"
  def find_scheduled(pid, jid) do
    GenServer.call(pid, {:find_scheduled, jid})
  end

  @doc """
  Find job in scheduled queue

  Expected args:
    * `pid` - Exq.Api process
    * `score` - Job score
    * `jid` - Job jid
    * `options`
      - raw: (boolean) whether to deserialize the job

  Returns:
    * `{:ok, job}`

  """
  def find_scheduled(pid, score, jid, options \\ []) do
    GenServer.call(pid, {:find_scheduled, score, jid, options})
  end

  @doc """
  Removes a job scheduled to run in the future from being enqueued.

  Expected args:
    * `pid` - Exq.Api process
    * `jid` - Unique identifier for the job

  Returns:
    * `:ok`

  """
  @deprecated "use remove_scheduled_jobs/2"
  def remove_scheduled(pid, jid) do
    GenServer.call(pid, {:remove_scheduled, jid})
  end

  @doc """
  Removes jobs from scheduled queue.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values
    * `options`
      - clear_unique_tokens: (boolean) whether to clear jobs unique tokens, default is `false`

  Returns:
    * `:ok`

  """
  def remove_scheduled_jobs(pid, raw_jobs, options \\ []) do
    GenServer.call(pid, {:remove_scheduled_jobs, raw_jobs, options})
  end

  def clear_scheduled(pid) do
    GenServer.call(pid, :clear_scheduled)
  end

  @doc """
  Enqueue jobs from scheduled queue immediately.

  Expected args:
    * `pid` - Exq.Api process
    * `raw_jobs` - list of raw json encoded job values

  Returns:
    * `{:ok, num_enqueued}`

  """
  def dequeue_scheduled_jobs(pid, raw_jobs) do
    GenServer.call(pid, {:dequeue_scheduled_jobs, raw_jobs})
  end

  @doc """
  Number of scheduled jobs enqueued.

  Expected args:
    * `pid` - Exq.Api process

  Returns:
    * `{:ok, num_scheduled}` - number of scheduled jobs enqueued

  """
  def scheduled_size(pid) do
    GenServer.call(pid, :scheduled_size)
  end

  @doc """
  Return stat for given key.

  Examples of keys are `processed` and `failed`.

  Expected args:
    * `pid` - Exq.Api process
    * `key` - Key for stat
    * `queue` - Queue name

  Returns:
    * `{:ok, stat}` stat for key

  """
  def stats(pid, key) do
    GenServer.call(pid, {:stats, key})
  end

  def stats(pid, key, dates) when is_list(dates) do
    GenServer.call(pid, {:stats, key, dates})
  end

  def stats(pid, key, date) do
    with {:ok, [count]} <- GenServer.call(pid, {:stats, key, [date]}) do
      {:ok, count}
    end
  end

  def realtime_stats(pid) do
    GenServer.call(pid, :realtime_stats)
  end

  def retry_job(pid, jid) do
    GenServer.call(pid, {:retry_job, jid})
  end

  @doc """
  Send signal to the given node.

  Expected args:
    * `pid` - Exq.Api process
    * `node_id` - node identifier
    * `signal_name` - Name of the signal.

  Supported Signals
    * TSTP - unsubscibe from all queues

  Returns:
    * :ok
  """
  def send_signal(pid, node_id, signal_name) do
    GenServer.call(pid, {:send_signal, node_id, signal_name})
  end
end
