defmodule Microwaveprop.Weather.NexradCache do
  @moduledoc """
  Node-local ETS cache of decoded NEXRAD n0q composite reflectivity frames.
  Keyed by a 5-minute rounded timestamp. Stores the raw pixel buffer + image
  width so per-point rain-cell extraction can skip the HTTP fetch + PNG decode
  (which take 1-5 seconds for a ~5 MB CONUS-wide image).

  The cache is populated by `Microwaveprop.Weather.NexradClient.fetch_rain_cells/4`
  on its first call per 5-min window, then reused by every concurrent click
  until the window rolls over.
  """
  use GenServer

  @table :nexrad_frame_cache

  @type pixels :: binary()
  @type width :: pos_integer()

  @spec start_link(keyword()) :: GenServer.on_start()
  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
  end

  @spec fetch(DateTime.t()) :: {:ok, pixels(), width()} | :miss
  def fetch(rounded_ts) do
    case :ets.lookup(@table, rounded_ts) do
      [{_, pixels, width}] -> {:ok, pixels, width}
      [] -> :miss
    end
  end

  @spec put(DateTime.t(), pixels(), width()) :: :ok
  def put(rounded_ts, pixels, width) do
    :ets.insert(@table, {rounded_ts, pixels, width})
    :ok
  end

  @spec prune_older_than(DateTime.t()) :: non_neg_integer()
  def prune_older_than(cutoff_ts) do
    match_spec = [{{:"$1", :_, :_}, [{:<, :"$1", {:const, cutoff_ts}}], [true]}]
    :ets.select_delete(@table, match_spec)
  end

  @spec clear() :: :ok
  def clear do
    :ets.delete_all_objects(@table)
    :ok
  end

  @impl true
  def init(_opts) do
    :ets.new(@table, [:set, :named_table, :public, read_concurrency: true])
    {:ok, %{}}
  end
end
