lib/fennel/cache.ex

defmodule Fennel.Cache do
  require Logger

  alias Fennel.{Notifier, Operation, Telemetry}
  alias Fennel.Cache.{Observer, Data, Normalizer, Denormalizer}

  # Cache should be Absinthe plugin

  def name(), do: __MODULE__

  def current() do
    # TODO: Optimize it. Save in process/registry info that it had been registered?
    :ok = Observer.watch_me(Data.prefix())
    name()
  end

  # def entity_key(client, _entity) do
  #   {:error, :missing}
  # end

  # def resolve(client, _entity, _field, _args \\ []) do
  # end

  # def invalidate(client, _entity, _field \\ nil, _args \\ []) do
  # end

  def update_operation(_client, operation, updater) do
    update_operation(operation, %{}, updater)
  end

  def update_operation(_client, _operation, _variables, _updater) do
    # Data.get_and_update(current(), build_operation_key(operation, variables))
    # |> case do
    #   {:ignore, err} -> err
    #   val -> val
    # end
  end

  def read_operation(ctx, operation) do
    operation_key = build_operation_key(operation, ctx.variables)

    Data.fetch(current(), operation_key)
    |> case do
      {:error, _err} = reply ->
        Telemetry.cache(:miss, operation_key)
        reply

      {:ok, val} ->
        Telemetry.cache(:hit, operation_key)

        Denormalizer.call(ctx.client, operation, val)
    end
  end

  def write_operation(ctx, operation, response) do
    operation_key = build_operation_key(operation, ctx.variables)
    {keys_values, metadata} = Normalizer.call(ctx.client, operation, operation_key, response)

    Data.put(current(), keys_values)
    |> case do
      {:ok, true} ->
        Telemetry.cache(:write, operation_key)

        Notifier.updated_cache_entities(ctx, operation, {
          keys_values,
          metadata
        })

        :ok
    end
  end

  # Updates entity partially
  # def read_fragment(client, _operation, _variables) do
  # end

  # def write_fragment(client, _operation, _variables) do
  # end

  def build_operation_key(operation, variables) do
    query = Operation.to_query(operation)

    [query, variables]
  end
end