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

  alias Microwaveprop.Propagation.HfMuf

  describe "adjust_mufd/2" do
    test "returns 0.0 for nil or non-positive inputs" do
      assert HfMuf.adjust_mufd(nil, 2000) == 0.0
      assert HfMuf.adjust_mufd(23.0, nil) == 0.0
      assert HfMuf.adjust_mufd(0.0, 2000) == 0.0
      assert HfMuf.adjust_mufd(23.0, 0) == 0.0
    end

    test "is a no-op at the reference distance (3000 km)" do
      # MUFD is *defined* at the 3000 km GIRO DIDBase reference distance.
      assert_in_delta HfMuf.adjust_mufd(23.0, 3000), 23.0, 0.001
    end

    test "shorter paths have lower MUF (closer to vertical incidence)" do
      short = HfMuf.adjust_mufd(23.0, 1500)
      medium = HfMuf.adjust_mufd(23.0, 2500)
      ref = HfMuf.adjust_mufd(23.0, 3000)
      assert short < medium
      assert medium < ref
    end

    test "longer paths within single-hop window have higher MUF" do
      ref = HfMuf.adjust_mufd(23.0, 3000)
      long = HfMuf.adjust_mufd(23.0, 4000)
      very_long = HfMuf.adjust_mufd(23.0, 5000)
      assert long > ref
      assert very_long > long
    end

    test "vertical-incidence limit approaches foF2 (roughly 1/sec(i_ref) × MUFD)" do
      # As distance → 0 the ray is vertical and MUF → foF2.
      # For h=300, sec(i_ref) ≈ 5.10, so at D=0 the scaled MUFD should
      # be about MUFD / 5.10 ≈ foF2.
      tiny = HfMuf.adjust_mufd(23.0, 1)
      assert_in_delta tiny, 23.0 / 5.099, 0.1
    end
  end

  describe "fot/1" do
    test "returns 0.85 × MUF" do
      assert HfMuf.fot(20.0) == 17.0
      assert_in_delta HfMuf.fot(28.35), 24.1, 0.01
    end

    test "returns 0.0 for nil or non-positive inputs" do
      assert HfMuf.fot(nil) == 0.0
      assert HfMuf.fot(0.0) == 0.0
      assert HfMuf.fot(-5.0) == 0.0
    end
  end

  describe "hf_score/2" do
    test "returns 0 for nil or non-positive MUF" do
      assert HfMuf.hf_score(nil, 14.0) == 0
      assert HfMuf.hf_score(0.0, 14.0) == 0
    end

    test "returns 0 when band is well above MUF" do
      # 28 MHz on a path with MUF=15 MHz — 10m is dead.
      assert HfMuf.hf_score(15.0, 28.0) == 0
    end

    test "returns 100 when band is comfortably below FOT" do
      # 7 MHz on a path with MUF=30 MHz — 40m is solid, way below FOT (25.5).
      assert HfMuf.hf_score(30.0, 7.0) == 100
    end

    test "returns 80 near FOT (85% of MUF)" do
      # Band ≈ 0.85 × MUF should land on FOT bucket.
      assert HfMuf.hf_score(20.0, 17.0) == 80
    end

    test "returns 50 between FOT and MUF" do
      # Band at 0.95 × MUF is marginal.
      assert HfMuf.hf_score(20.0, 19.0) == 50
    end

    test "returns 20 in the fringe zone slightly above MUF" do
      # Band 10% above MUF — fading, spotty, but sometimes there.
      assert HfMuf.hf_score(20.0, 22.0) == 20
    end

    test "is monotonic: higher MUF never decreases the score at a fixed band" do
      # 14 MHz across a range of MUFs.
      scores = Enum.map([10, 14, 16, 18, 25, 40], &HfMuf.hf_score(&1 * 1.0, 14.0))

      # Each score must be ≥ the previous.
      Enum.reduce(scores, 0, fn s, prev ->
        assert s >= prev
        s
      end)
    end
  end
end
