defmodule Microwaveprop.Rover.ElevationTest do
  use ExUnit.Case, async: false

  alias Microwaveprop.Rover.Elevation

  setup do
    tmp = Path.join(System.tmp_dir!(), "rover_elev_#{System.unique_integer([:positive])}")
    File.mkdir_p!(tmp)
    prev = Application.get_env(:microwaveprop, :srtm_tiles_dir)
    Application.put_env(:microwaveprop, :srtm_tiles_dir, tmp)

    on_exit(fn ->
      File.rm_rf(tmp)

      if prev do
        Application.put_env(:microwaveprop, :srtm_tiles_dir, prev)
      else
        Application.delete_env(:microwaveprop, :srtm_tiles_dir)
      end
    end)

    :ok
  end

  describe "lookup_many/1" do
    test "returns map keyed by {lat, lon}, nil values when no tiles available" do
      points = [{33.0, -96.0}, {30.0, -97.0}]
      result = Elevation.lookup_many(points)

      assert is_map(result)
      assert Map.has_key?(result, {33.0, -96.0})
      assert Map.has_key?(result, {30.0, -97.0})
      assert result[{33.0, -96.0}] == nil
      assert result[{30.0, -97.0}] == nil
    end

    test "empty list returns empty map" do
      assert Elevation.lookup_many([]) == %{}
    end

    test "duplicate points are deduplicated before lookup" do
      points = [{33.0, -96.0}, {33.0, -96.0}, {33.1, -97.0}]
      result = Elevation.lookup_many(points)

      assert map_size(result) == 2
    end

    test "points on different tiles each get an entry" do
      points = [{45.0, -120.0}, {30.0, -90.0}]
      result = Elevation.lookup_many(points)

      assert map_size(result) == 2
      assert result[{45.0, -120.0}] == nil
      assert result[{30.0, -90.0}] == nil
    end

    test "reads elevation from a valid SRTM tile file" do
      # SRTM tile at lat~0, lon~0 (tile N00E000.hgt) stores 3601×3601
      # 16-bit big-endian signed integers. Write a minimal valid tile
      # with known elevation at the first sample point.
      tile_path = Path.join(Application.get_env(:microwaveprop, :srtm_tiles_dir), "N00E000.hgt")
      elev_value = 1234
      # Build enough binary to cover offset 0 (row=0, col=0 = first sample)
      binary = <<elev_value::signed-big-integer-size(16)>>
      File.write!(tile_path, binary)

      # lat=0.9999, lon=0.0 → row=0, col=0 → offset=0
      result = Elevation.lookup_many([{0.9999, 0.0}])
      assert result[{0.9999, 0.0}] == elev_value
    end

    test "returns nil for SRTM void sentinel values (-32768)" do
      tile_path = Path.join(Application.get_env(:microwaveprop, :srtm_tiles_dir), "N00E000.hgt")
      # SRTM void sentinel is -32768
      File.write!(tile_path, <<(-32_768)::signed-big-integer-size(16)>>)

      result = Elevation.lookup_many([{0.9999, 0.0}])
      assert result[{0.9999, 0.0}] == nil
    end
  end
end
