defmodule Microwaveprop.Rover.Hilltop do
  @moduledoc """
  Snaps a propagation-grid cell center to the highest SRTM point inside
  its footprint. The propagation grid is on a 0.125° lattice (~14 km),
  but a rover wants to park on the actual hilltop inside that cell, not
  at the nominal cell center. Sample SRTM at sub-cell resolution and
  return the highest point.
  """

  alias Microwaveprop.Rover.Elevation

  @default_half_deg 0.0625
  @default_step_deg 0.01

  @type latlon :: {float(), float()}

  @doc """
  Returns `{best_lat, best_lon, best_elev}` for the highest SRTM point
  inside `±half_deg` of `cell_center`, or `nil` if SRTM coverage is
  missing across the whole footprint.
  """
  @spec snap(latlon(), keyword()) :: {float(), float(), integer()} | nil
  def snap({lat, lon}, opts \\ []) do
    half = Keyword.get(opts, :half_deg, @default_half_deg)
    step = Keyword.get(opts, :step_deg, @default_step_deg)
    elev_lookup = Keyword.get(opts, :elev_lookup, &Elevation.lookup_many/1)

    points = sample_grid(lat, lon, half, step)
    elev_map = elev_lookup.(points)

    points
    |> Enum.flat_map(fn pt ->
      case Map.get(elev_map, pt) do
        nil -> []
        elev -> [{pt, elev}]
      end
    end)
    |> case do
      [] ->
        nil

      list ->
        {{best_lat, best_lon}, best_elev} = Enum.max_by(list, fn {_, e} -> e end)
        {best_lat, best_lon, best_elev}
    end
  end

  defp sample_grid(lat, lon, half, step) do
    n = max(trunc(half / step), 1)

    for i <- -n..n, j <- -n..n do
      {Float.round(lat + i * step, 4), Float.round(lon + j * step, 4)}
    end
  end
end
