defmodule Chord.Cleanup.Server do
  @moduledoc """
  Periodic cleanup server for stale contexts and deltas.

  This server periodically triggers the cleanup logic defined in `Chord.Cleanup`,
  removing stale entries from the backend.

  ## State
  The server's state includes:
    - `:interval` - The interval (in milliseconds) between cleanup executions.
    - `:backend_opts` - A keyword list of options passed to backend listing functions.

  ## Example

      Chord.Cleanup.Server.start_link(interval: :timer.minutes(30), backend_opts: [limit: 100])
  """

  use GenServer
  require Logger
  alias Chord.Cleanup

  @default_interval :timer.hours(1)

  @doc """
  Starts the cleanup server with the specified options.

  ## Options
    - `:interval` (integer): Time interval in milliseconds for periodic cleanup (default: 1 hour).
    - `:backend_opts` (Keyword.t): Options passed to backend listing functions (see "Common Options" in the `Chord` module).

  ## Example

      iex> Chord.Cleanup.Server.start_link(interval: :timer.minutes(30), backend_opts: [limit: 50])
      {:ok, pid}
  """
  def start_link(opts \\ []) do
    GenServer.start_link(__MODULE__, opts, name: Keyword.get(opts, :name, __MODULE__))
  end

  @doc """
  Updates the interval for periodic cleanup at runtime.

  ## Parameters
    - `new_interval` (integer): The new interval in milliseconds.
    - `name` (atom): The name of the `GenServer` instance. Defaults to `__MODULE__`.


  ## Returns
    - `:ok` if the interval was successfully updated.
  """
  def update_interval(new_interval, name \\ __MODULE__) do
    GenServer.call(name, {:update_interval, new_interval})
  end

  @doc """
  Updates the backend options for periodic cleanup at runtime.

  ## Parameters
    - `new_opts` (Keyword.t): The new backend options.
    - `name` (atom): The name of the `GenServer` instance. Defaults to `__MODULE__`.


  ## Returns
    - `:ok` if the backend options were successfully updated.
  """
  def update_backend_opts(new_opts, name \\ __MODULE__) do
    GenServer.call(name, {:update_backend_opts, new_opts})
  end

  @impl true
  def init(opts) do
    interval = Keyword.get(opts, :interval, @default_interval)
    backend_opts = Keyword.get(opts, :backend_opts, [])

    Logger.info(
      "Cleanup server started with interval: #{interval} ms and options: #{inspect(opts)}"
    )

    schedule_cleanup(interval)
    {:ok, %{interval: interval, backend_opts: backend_opts}}
  end

  @impl true
  def handle_call({:update_interval, new_interval}, _from, state) do
    Logger.info("Updating cleanup interval from #{state.interval} ms to #{new_interval} ms")
    {:reply, :ok, %{state | interval: new_interval}}
  end

  @impl true
  def handle_call({:update_backend_opts, new_opts}, _from, state) do
    Logger.info(
      "Updating backend options from #{inspect(state.backend_opts)} to #{inspect(new_opts)}"
    )

    {:reply, :ok, %{state | backend_opts: new_opts}}
  end

  @impl true
  def handle_info(:cleanup, state) do
    opts = state.backend_opts
    Logger.info("Starting cleanup operation with state: #{inspect(state)}")

    Cleanup.periodic_cleanup(opts)

    schedule_cleanup(state.interval)
    {:noreply, state}
  end

  def handle_info(_unknown_message, state) do
    Logger.warning("Received unknown message in cleanup server")
    {:noreply, state}
  end

  @doc false
  defp schedule_cleanup(interval) do
    Logger.debug("Scheduling next cleanup operation in #{interval} ms")
    Process.send_after(self(), :cleanup, interval)
  end
end
