defmodule Microwaveprop.Terrain.ElevationClientTest do
  use ExUnit.Case, async: true

  alias Microwaveprop.Terrain.ElevationClient

  describe "sample_path/5" do
    test "generates N+1 evenly spaced points" do
      points = ElevationClient.sample_path(32.9, -97.0, 30.3, -97.7, 4)

      assert length(points) == 5

      first = hd(points)
      last = List.last(points)

      assert_in_delta first.lat, 32.9, 0.001
      assert_in_delta first.lon, -97.0, 0.001
      assert_in_delta first.d, 0.0, 0.001

      assert_in_delta last.lat, 30.3, 0.001
      assert_in_delta last.lon, -97.7, 0.001
      assert_in_delta last.d, 1.0, 0.001
    end

    test "midpoint is correct" do
      points = ElevationClient.sample_path(0.0, 0.0, 10.0, 10.0, 2)

      mid = Enum.at(points, 1)
      assert_in_delta mid.lat, 5.0, 0.001
      assert_in_delta mid.lon, 5.0, 0.001
      assert_in_delta mid.d, 0.5, 0.001
    end
  end

  describe "fetch_elevation_profile/5" do
    test "returns profile with elevations from Open-Meteo" do
      Req.Test.stub(ElevationClient, fn conn ->
        Req.Test.json(conn, %{"elevation" => [200.0, 250.0, 180.0]})
      end)

      assert {:ok, profile} =
               ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)

      assert length(profile) == 3

      first = hd(profile)
      assert Map.has_key?(first, :lat)
      assert Map.has_key?(first, :lon)
      assert Map.has_key?(first, :elev)
      assert Map.has_key?(first, :dist_km)
      assert first.elev == 200.0
      assert_in_delta first.dist_km, 0.0, 0.001
    end

    test "batches requests when more than 100 points" do
      call_count = :counters.new(1, [:atomics])

      Req.Test.stub(ElevationClient, fn conn ->
        :counters.add(call_count, 1, 1)
        # Return 101 elevations for first batch, or however many requested
        params = Plug.Conn.fetch_query_params(conn).query_params
        lat_count = params["latitude"] |> String.split(",") |> length()
        elevations = List.duplicate(100.0, lat_count)
        Req.Test.json(conn, %{"elevation" => elevations})
      end)

      assert {:ok, profile} =
               ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 150)

      # 151 points → 2 batches (100 + 51)
      assert length(profile) == 151
      assert :counters.get(call_count, 1) == 2
    end

    test "falls back to Open-Topo-Data on Open-Meteo failure" do
      call_count = :counters.new(1, [:atomics])

      Req.Test.stub(ElevationClient, fn conn ->
        :counters.add(call_count, 1, 1)
        current = :counters.get(call_count, 1)

        if current == 1 do
          # First call (Open-Meteo) fails
          Plug.Conn.send_resp(conn, 500, "error")
        else
          # Second call (Open-Topo-Data) succeeds
          Req.Test.json(conn, %{
            "status" => "OK",
            "results" => [
              %{"elevation" => 100.0},
              %{"elevation" => 150.0},
              %{"elevation" => 120.0}
            ]
          })
        end
      end)

      assert {:ok, profile} =
               ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)

      assert length(profile) == 3
      assert hd(profile).elev == 100.0
    end

    test "returns error when both APIs fail" do
      Req.Test.stub(ElevationClient, fn conn ->
        Plug.Conn.send_resp(conn, 500, "error")
      end)

      assert {:error, _reason} =
               ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
    end
  end
end

defmodule Microwaveprop.Terrain.ElevationClientSrtmTest do
  use ExUnit.Case, async: false

  alias Microwaveprop.Terrain.ElevationClient

  @tiles_dir Path.expand("~/srtm/tiles")
  @has_tiles File.exists?(Path.join(@tiles_dir, "N32W097.hgt"))

  setup do
    prev = Application.get_env(:microwaveprop, :srtm_tiles_dir)

    on_exit(fn ->
      if prev,
        do: Application.put_env(:microwaveprop, :srtm_tiles_dir, prev),
        else: Application.delete_env(:microwaveprop, :srtm_tiles_dir)
    end)

    :ok
  end

  describe "SRTM integration" do
    @tag :srtm
    @tag skip: !@has_tiles && "SRTM tiles not available"
    test "uses local SRTM tiles when configured" do
      Application.put_env(:microwaveprop, :srtm_tiles_dir, @tiles_dir)

      assert {:ok, profile} =
               ElevationClient.fetch_elevation_profile(32.78, -96.8, 32.5, -96.5, 4)

      assert length(profile) == 5
      assert hd(profile).elev > 0
    end

    @tag :srtm
    test "returns zero elevation when SRTM tiles missing (no API fallback)" do
      Application.put_env(:microwaveprop, :srtm_tiles_dir, "/nonexistent/tiles")

      assert {:ok, profile} =
               ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)

      assert length(profile) == 3
      assert Enum.all?(profile, fn p -> p.elev == 0 end)
    end
  end
end
