defmodule Microwaveprop.Beacons.RangeEstimateTest do
  use Microwaveprop.DataCase

  alias Microwaveprop.Beacons.Beacon
  alias Microwaveprop.Beacons.RangeEstimate

  defp beacon(overrides \\ %{}) do
    base = %Beacon{
      id: "00000000-0000-0000-0000-000000000000",
      frequency_mhz: 10_368.1,
      callsign: "W5HN",
      grid: "EM12",
      lat: 32.897,
      lon: -97.038,
      power_mw: 10_000.0,
      height_ft: 100,
      on_the_air: true
    }

    struct!(base, overrides)
  end

  describe "mw_to_dbm/1" do
    test "1 mW is 0 dBm" do
      assert_in_delta RangeEstimate.mw_to_dbm(1.0), 0.0, 0.001
    end

    test "1 W (1000 mW) is 30 dBm" do
      assert_in_delta RangeEstimate.mw_to_dbm(1000.0), 30.0, 0.001
    end

    test "10 W (10_000 mW) is 40 dBm" do
      assert_in_delta RangeEstimate.mw_to_dbm(10_000.0), 40.0, 0.001
    end
  end

  describe "nearest_band_mhz/1" do
    test "10368.1 MHz maps to 10000 (10 GHz band)" do
      assert RangeEstimate.nearest_band_mhz(10_368.1) == 10_000
    end

    test "24192.1 MHz maps to 24000" do
      assert RangeEstimate.nearest_band_mhz(24_192.1) == 24_000
    end

    test "47088 MHz maps to 47000" do
      assert RangeEstimate.nearest_band_mhz(47_088.0) == 47_000
    end
  end

  describe "estimate/1" do
    test "returns a map with band info, center score, and per-grid cells" do
      result = RangeEstimate.estimate(beacon())

      assert result.band_mhz == 10_000
      assert is_integer(result.center_score) or is_float(result.center_score)
      assert is_float(result.eirp_dbm)
      assert is_list(result.cells)
      assert result.cells != []
      assert result.grid_step == 0.125
      assert is_list(result.tiers)
    end

    test "each cell carries lat/lon, distance, rx_dbm, tier label/color" do
      result = RangeEstimate.estimate(beacon())

      for cell <- result.cells do
        assert is_float(cell.lat)
        assert is_float(cell.lon)
        assert is_float(cell.distance_km)
        assert is_integer(cell.rx_dbm)
        assert is_binary(cell.label)
        assert is_binary(cell.color)
        # Detection floor enforced
        assert cell.rx_dbm >= -145
      end
    end

    test "cells closer to the beacon have stronger rx_dbm" do
      result = RangeEstimate.estimate(beacon())

      [closest | _] = Enum.sort_by(result.cells, & &1.distance_km)
      [farthest | _] = Enum.sort_by(result.cells, &(-&1.distance_km))

      assert closest.rx_dbm > farthest.rx_dbm
    end

    test "a more powerful beacon produces more cells" do
      weak = RangeEstimate.estimate(beacon(power_mw: 10.0))
      strong = RangeEstimate.estimate(beacon(power_mw: 10_000.0))
      assert length(strong.cells) > length(weak.cells)
    end

    test "uses a default score of 50 when no propagation data exists" do
      result = RangeEstimate.estimate(beacon())
      # No scores in test DB → center defaults, cells all use 50.
      assert result.center_score == 50
      assert result.valid_time == nil

      # All cells should have score 50 when there's no HRRR data.
      assert Enum.all?(result.cells, fn c -> c.score == 50 end)
    end

    test "cells align with the 0.125° HRRR grid" do
      result = RangeEstimate.estimate(beacon())

      for cell <- result.cells do
        # lat and lon should both be multiples of 0.125
        lat_mod = abs(cell.lat / 0.125 - round(cell.lat / 0.125))
        lon_mod = abs(cell.lon / 0.125 - round(cell.lon / 0.125))
        assert lat_mod < 0.01
        assert lon_mod < 0.01
      end
    end

    test "short-circuits with empty cells for beacons below 5.76 GHz" do
      # 432 MHz with 1 W at unrestricted range would generate >100k cells
      # because atmospheric loss is negligible — encoding that payload
      # into the map's data attribute was locking up browsers in prod.
      result = RangeEstimate.estimate(beacon(frequency_mhz: 432.38, power_mw: 1000.0))
      assert result.cells == []
      assert result.max_range_km == 0.0
      # Still reports the nearest configured band for display purposes.
      assert is_integer(result.band_mhz)
    end

    test "short-circuits at exactly 5760 MHz threshold" do
      above = RangeEstimate.estimate(beacon(frequency_mhz: 5760.0, power_mw: 1000.0))
      below = RangeEstimate.estimate(beacon(frequency_mhz: 5759.9, power_mw: 1000.0))
      assert above.cells != []
      assert below.cells == []
    end
  end
end
