lib/kaffy/scheduler/task.ex

defmodule Kaffy.Scheduler.Task do
  use GenServer

  def start_link(args) do
    GenServer.start_link(__MODULE__, args)
  end

  @impl true
  def init(task) do
    task =
      Map.merge(task, %{
        successful: 0,
        failure: 0,
        failure_value: nil,
        last_successful: nil,
        last_failure: nil,
        current_value: Map.get(task, :initial_value),
        started_at: DateTime.utc_now()
      })

    schedule_work(task)
    {:ok, task}
  end

  @impl true
  def handle_info(:perform_task, task) do
    task =
      case task.action.(task.current_value) do
        {:ok, value} ->
          task
          |> Map.put(:current_value, value)
          |> Map.put(:successful, task.successful + 1)
          |> Map.put(:last_successful, DateTime.utc_now())

        {:error, invalid_value} ->
          task
          |> Map.put(:failure, task.failure + 1)
          |> Map.put(:failure_value, invalid_value)
          |> Map.put(:last_failure, DateTime.utc_now())
      end

    # Reschedule once more
    schedule_work(task)
    {:noreply, task}
  end

  @impl true
  def handle_call(:info, _, task) do
    {:reply, task, task}
  end

  defp schedule_work(task) do
    # In seconds
    Process.send_after(self(), :perform_task, task.every * 1000)
  end
end