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

  alias Microwaveprop.Propagation.SporadicE

  describe "single_hop_muf/2" do
    test "returns 0.0 for nil or non-positive inputs" do
      assert SporadicE.single_hop_muf(nil, 2000) == 0.0
      assert SporadicE.single_hop_muf(5.0, nil) == 0.0
      assert SporadicE.single_hop_muf(0.0, 2000) == 0.0
      assert SporadicE.single_hop_muf(5.0, 0) == 0.0
      assert SporadicE.single_hop_muf(-1.0, 2000) == 0.0
    end

    test "matches the thin-layer secant-of-incidence formula for known distances" do
      # h = 110 km. sec(i) = sqrt(1 + (D/(2h))^2). Values cross-checked
      # against literature (Davies 1990, Ionospheric Radio Eq. 6.26).
      # At D=2000 km, sec(i) ≈ 9.146, so foEs × 9.146 MHz.
      assert_in_delta SporadicE.single_hop_muf(10.0, 2000), 10.0 * 9.146, 0.05
      assert_in_delta SporadicE.single_hop_muf(5.0, 1000), 5.0 * 4.654, 0.05
      assert_in_delta SporadicE.single_hop_muf(8.0, 1500), 8.0 * 6.891, 0.05
    end

    test "degenerate short range approaches foEs (vertical incidence)" do
      # As D → 0 the ray is nearly vertical and MUF → foEs.
      assert_in_delta SporadicE.single_hop_muf(5.0, 1), 5.0, 0.001
    end
  end

  describe "es_score/3" do
    test "is 0 when foEs is nil or zero" do
      assert SporadicE.es_score(nil, 144, 2000) == 0
      assert SporadicE.es_score(0.0, 144, 2000) == 0
    end

    test "is 0 for paths shorter than the single-hop minimum" do
      # Es geometry doesn't support paths much under ~500 km (the
      # elevation angle is too steep for the layer to reflect a VHF
      # signal regardless of foEs).
      assert SporadicE.es_score(20.0, 144, 100) == 0
      assert SporadicE.es_score(20.0, 144, 400) == 0
    end

    test "is 0 for paths beyond the single-hop maximum (~2500 km)" do
      # Beyond ~2500 km the geometry exits the single-hop window. We
      # don't model multi-hop Es here — that's a different factor.
      assert SporadicE.es_score(20.0, 144, 3000) == 0
    end

    test "scores 50 MHz as ROUTINE when foEs is typical summer Es (6-8 MHz) at 2000 km" do
      # foEs 6 MHz × sec(i) at 2000 km ≈ 54.9 MHz, just above 50 MHz.
      # 50 MHz Es at 2000 km with foEs=6 is a classic summer opening —
      # should score high.
      score = SporadicE.es_score(6.0, 50, 2000)
      assert score >= 80
    end

    test "scores 144 MHz as NONE when foEs is typical summer Es" do
      # foEs=6 at 2000 km ≈ 54.9 MHz MUF — well below 144 MHz.
      # Typical summer Es doesn't propagate 2m.
      assert SporadicE.es_score(6.0, 144, 2000) == 0
    end

    test "scores 144 MHz as VIABLE when foEs reaches intense-Es levels (16+ MHz) at 2000 km" do
      # foEs=16 × 9.15 ≈ 146 MHz MUF → 2m Es is feasible. These are
      # rare but documented events (June/July peaks).
      score = SporadicE.es_score(16.0, 144, 2000)
      assert score >= 50
    end

    test "scores 432 MHz as NONE even at extreme foEs (sporadic-E does not reach 70cm)" do
      # foEs would need to be ~47 MHz to get 432 MHz via single-hop Es
      # — way beyond any physically observed value.
      assert SporadicE.es_score(25.0, 432, 2000) == 0
    end

    test "higher foEs strictly increases the score at a given band/distance (monotonic)" do
      weak = SporadicE.es_score(10.0, 50, 2000)
      mid = SporadicE.es_score(14.0, 50, 2000)
      strong = SporadicE.es_score(20.0, 50, 2000)
      assert weak <= mid
      assert mid <= strong
      assert strong == 100
    end
  end
end
