defmodule Microwaveprop.GeoTest do
  use ExUnit.Case, async: true

  alias Microwaveprop.Geo
  alias Microwaveprop.Radio.Maidenhead

  describe "haversine_km/4" do
    test "returns 0.0 for identical points" do
      assert Geo.haversine_km(32.9, -96.8, 32.9, -96.8) == 0.0
    end

    test "matches a known long-distance pair (DFW to Newington, CT)" do
      assert_in_delta Geo.haversine_km(32.9, -96.8, 41.714, -72.727), 2333.3, 1.0
    end

    test "computes a half-circumference for antipodal equator points" do
      # Half the Earth's circumference at R = 6371 km is ~20015 km.
      assert_in_delta Geo.haversine_km(0.0, 0.0, 0.0, 180.0), 20_015.0, 1.0
    end

    test "is symmetric in its arguments" do
      forward = Geo.haversine_km(32.9, -96.8, 41.714, -72.727)
      reverse = Geo.haversine_km(41.714, -72.727, 32.9, -96.8)
      assert_in_delta forward, reverse, 0.0001
    end

    test "one degree of latitude is roughly 111 km" do
      assert_in_delta Geo.haversine_km(32.0, -96.0, 33.0, -96.0), 111.195, 0.1
    end
  end

  describe "bearing_deg/4" do
    test "returns 0.0 for due north" do
      assert Geo.bearing_deg(32.0, -96.0, 33.0, -96.0) == 0.0
    end

    test "returns ~90 for due east at the equator" do
      assert_in_delta Geo.bearing_deg(0.0, 0.0, 0.0, 1.0), 90.0, 0.1
    end

    test "returns 180.0 for due south" do
      assert Geo.bearing_deg(32.0, -96.0, 31.0, -96.0) == 180.0
    end

    test "returns a bearing in the range [0, 360)" do
      bearing = Geo.bearing_deg(32.0, -96.0, 32.0, -97.0)
      assert bearing >= 0.0 and bearing < 360.0
      assert_in_delta bearing, 270.0, 1.0
    end

    test "rounds to one decimal place" do
      # DFW to Newington - pick any pair, the result must have at most 1 decimal.
      bearing = Geo.bearing_deg(32.9, -96.8, 41.714, -72.727)
      assert Float.round(bearing, 1) == bearing
    end
  end

  describe "maidenhead_center/1" do
    test "returns the center of a 4-character grid" do
      # EM12 is lat 32-33, lon -98 to -96, center (32.5, -97.0).
      assert Geo.maidenhead_center("EM12") == {32.5, -97.0}
    end

    test "returns the center of a 6-character grid at higher precision" do
      {lat, lon} = Geo.maidenhead_center("EM12kp")
      assert_in_delta lat, 32.6458, 0.001
      assert_in_delta lon, -97.125, 0.001
    end

    test "accepts lowercase grids" do
      assert Geo.maidenhead_center("em12") == {32.5, -97.0}
    end

    test "returns nil for invalid grid characters" do
      assert Geo.maidenhead_center("ZZZZ") == nil
    end
  end

  describe "latlon_to_grid4/2" do
    test "encodes the DFW area as EM12" do
      assert Geo.latlon_to_grid4(32.9, -96.8) == "EM12"
    end

    test "encodes Newington, CT as FN31" do
      assert Geo.latlon_to_grid4(41.714, -72.727) == "FN31"
    end

    test "encodes the prime-meridian/equator origin as JJ00" do
      assert Geo.latlon_to_grid4(0.0, 0.0) == "JJ00"
    end

    test "agrees with Maidenhead.from_latlon/3 at precision 4 for typical US coords" do
      for {lat, lon} <- [{32.9, -96.8}, {41.714, -72.727}, {40.0, -105.0}, {47.6, -122.3}] do
        assert Geo.latlon_to_grid4(lat, lon) == Maidenhead.from_latlon(lat, lon, 4)
      end
    end
  end
end
