lib/helper/setting.ex

defmodule MishkaInstaller.Helper.Setting do
  use GenServer
  require Logger
  @ets_table :setting_ets_state
  @sync_with_database 100_000
  @re_check_binding_db 10_000
  alias MishkaInstaller.Setting, as: DBSetting

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

  def push(config) do
    ETS.Set.put!(table(), {String.to_atom(config.name), config.configs})
  end

  def get(config_name) do
    case ETS.Set.get(table(), String.to_atom(config_name)) do
      {:ok, {_name, config}} -> config
      _ -> nil
    end
  end

  def get_all() do
    ETS.Set.to_list!(table())
  end

  def delete(config_name) do
    ETS.Set.delete(table(), String.to_atom(config_name))
  end

  def child_spec(process_name) do
    %{
      id: __MODULE__,
      start: {__MODULE__, :start_link, [process_name]},
      restart: :transient,
      max_restarts: 4
    }
  end

  @impl true
  def init(_state) do
    Logger.info("The ETS state of settings was staretd")
    Process.send_after(self(), :sync_with_database, @sync_with_database)

    table =
      ETS.Set.new!(
        name: @ets_table,
        protection: :public,
        read_concurrency: true,
        write_concurrency: true
      )

    {:ok, %{set: table}, {:continue, :sync_with_database}}
  end

  @impl true
  def handle_continue(:sync_with_database, state) do
    check_custom_pubsub_loaded(state)
  end

  @impl true
  def handle_info(:timeout, state) do
    Logger.info("We are waiting for your custom pubsub is loaded")
    check_custom_pubsub_loaded(state)
  end

  @impl true
  def handle_info(:sync_with_database, state) do
    Logger.info("Setting ETS state was synced with database")
    sync_with_database()
    Process.send_after(self(), :sync_with_database, @sync_with_database)
    {:noreply, state}
  end

  @impl true
  def handle_info({:setting, :ok, action, repo_data}, state) do
    Logger.warn("Your ETS state of setting is going to be updated")

    case action do
      :delete -> delete(repo_data.name)
      _ -> push(repo_data)
    end

    {:noreply, state}
  end

  @impl true
  def terminate(_reason, _state) do
    Logger.warn("Your ETS state of setting was restarted by a problem")
  end

  def sync_with_database() do
    DBSetting.settings()
    |> Enum.map(&push/1)
  end

  defp check_custom_pubsub_loaded(state) do
    custom_pubsub = MishkaInstaller.get_config(:pubsub)

    cond do
      !is_nil(custom_pubsub) && is_nil(Process.whereis(custom_pubsub)) ->
        {:noreply, state, 100}

      true ->
        Process.send_after(self(), :sync_with_database, @re_check_binding_db)
        MishkaInstaller.Setting.subscribe()
        {:noreply, state}
    end
  end

  defp table() do
    case ETS.Set.wrap_existing(@ets_table) do
      {:ok, set} ->
        set

      _ ->
        start_link([])
        table()
    end
  end
end