defmodule Aprsme.WeatherCache do
  @moduledoc """
  Persistent ETS-based cache for weather callsign lookups.

  Eliminates N+1 queries for `has_weather_packets?` checks during map
  marker rendering. Entries expire after `@ttl_ms` to stay reasonably
  current without per-callsign DB queries.
  """
  @cache_name :weather_cache
  @ttl_ms to_timeout(minute: 5)

  @doc """
  Create the ETS table if it doesn't exist yet.
  The table is typically created in application.ex alongside other ETS tables.
  """
  def setup do
    _table =
      if :ets.whereis(@cache_name) == :undefined do
        :ets.new(@cache_name, [
          :named_table,
          :public,
          :set,
          read_concurrency: true,
          write_concurrency: true
        ])
      end

    :ok
  rescue
    ArgumentError -> :ok
  end

  @doc """
  Batch-populate the cache for the given list of callsigns.
  """
  def cache_weather_callsigns(callsigns) when is_list(callsigns) do
    weather_set = Aprsme.Packets.weather_callsigns(callsigns)
    expires_at = System.monotonic_time(:millisecond) + @ttl_ms

    Enum.each(callsigns, fn cs ->
      key = String.upcase(String.trim(cs))
      has_weather = MapSet.member?(weather_set, key)
      :ets.insert(@cache_name, {key, has_weather, expires_at})
    end)

    :ok
  rescue
    _ -> :ok
  end

  @doc """
  Check if a callsign has weather packets.
  Returns `:unknown` if not cached, `true`/`false` if cached.
  """
  def weather_callsign?(callsign) when is_binary(callsign) do
    key = String.upcase(String.trim(callsign))

    case :ets.lookup(@cache_name, key) do
      [{^key, value, expires_at}] ->
        if System.monotonic_time(:millisecond) < expires_at do
          value
        else
          :ets.delete(@cache_name, key)
          :unknown
        end

      _ ->
        :unknown
    end
  end

  @doc """
  Evict expired entries.
  """
  def evict_expired do
    now = System.monotonic_time(:millisecond)

    @cache_name
    |> :ets.tab2list()
    |> Enum.each(fn
      {key, _value, expires_at} when expires_at < now ->
        :ets.delete(@cache_name, key)

      _ ->
        :ok
    end)

    :ok
  end
end
