defmodule Microwaveprop.Propagation.CommonVolumeTest do
  use ExUnit.Case, async: true

  alias Microwaveprop.Propagation.CommonVolume

  describe "in_common_volume?/4" do
    test "point at midpoint of two nearby stations is in the common volume" do
      pos1 = {32.9, -97.0}
      pos2 = {33.1, -96.8}
      midpoint = {33.0, -96.9}
      assert CommonVolume.in_common_volume?(pos1, pos2, midpoint, 400.0)
    end

    test "point far from both stations is not in the common volume" do
      pos1 = {32.9, -97.0}
      pos2 = {33.1, -96.8}
      far = {50.0, -50.0}
      refute CommonVolume.in_common_volume?(pos1, pos2, far, 400.0)
    end

    test "point inside circle-1 but outside circle-2 is not in the common volume" do
      pos1 = {33.0, -97.0}
      # ~900 km east so radius=400 circles don't overlap there
      pos2 = {33.0, -87.0}
      near_pos1 = {33.0, -96.5}
      assert CommonVolume.in_common_volume?(pos1, pos1, near_pos1, 400.0)
      refute CommonVolume.in_common_volume?(pos1, pos2, near_pos1, 400.0)
    end

    test "endpoints themselves are in the common volume when stations are < 2R apart" do
      pos1 = {32.9, -97.0}
      pos2 = {33.1, -96.8}
      assert CommonVolume.in_common_volume?(pos1, pos2, pos1, 400.0)
      assert CommonVolume.in_common_volume?(pos1, pos2, pos2, 400.0)
    end

    test "nothing is in the common volume when stations are > 2R apart" do
      pos1 = {33.0, -97.0}
      # ~2200 km east
      pos2 = {33.0, -73.0}
      refute CommonVolume.in_common_volume?(pos1, pos2, pos1, 400.0)
    end
  end

  describe "bounding_box/3" do
    test "returns min/max lat/lon covering the entire common volume" do
      pos1 = {32.9, -97.0}
      pos2 = {33.1, -96.8}
      bbox = CommonVolume.bounding_box(pos1, pos2, 400.0)

      # Sanity: midpoint is inside bbox
      assert bbox.min_lat <= 33.0 and bbox.max_lat >= 33.0
      assert bbox.min_lon <= -96.9 and bbox.max_lon >= -96.9

      # Bbox shouldn't extend past either individual circle bbox
      r_deg_lat = 400.0 / 111.32
      assert bbox.min_lat >= (pos1 |> elem(0) |> min(elem(pos2, 0))) - r_deg_lat - 0.01
      assert bbox.max_lat <= (pos1 |> elem(0) |> max(elem(pos2, 0))) + r_deg_lat + 0.01
    end

    test "returns an empty bbox when the circles don't overlap" do
      pos1 = {33.0, -97.0}
      pos2 = {33.0, -73.0}
      bbox = CommonVolume.bounding_box(pos1, pos2, 400.0)
      assert bbox == :empty
    end
  end

  describe "area_km2/3" do
    test "two coincident circles -> area of full circle" do
      pos = {33.0, -97.0}
      r = 400.0
      assert_in_delta CommonVolume.area_km2(pos, pos, r), :math.pi() * r * r, 1.0
    end

    test "circles just touching -> small area compared to full circle" do
      # ~800 km apart, radius 400 -> barely overlapping
      pos1 = {33.0, -97.0}
      pos2 = {33.0, -88.48}
      area = CommonVolume.area_km2(pos1, pos2, 400.0)
      full = :math.pi() * 400.0 * 400.0
      assert area >= 0.0
      assert area / full < 0.01
    end

    test "non-overlapping circles -> zero area" do
      pos1 = {33.0, -97.0}
      pos2 = {33.0, -73.0}
      assert CommonVolume.area_km2(pos1, pos2, 400.0) == 0.0
    end

    test "partially overlapping circles -> positive area less than a full circle" do
      pos1 = {33.0, -97.0}
      # ~400 km apart -> roughly 1/2 circle overlap
      pos2 = {33.0, -92.7}
      area = CommonVolume.area_km2(pos1, pos2, 400.0)
      full = :math.pi() * 400.0 * 400.0
      assert area > 0.0
      assert area < full
    end
  end
end
