defmodule Microwaveprop.Weather.SoundingParamsTest do
  use ExUnit.Case, async: true

  alias Microwaveprop.Weather.SoundingParams

  # Realistic 3-level profile (surface, 925, 850 hPa)
  @simple_profile [
    %{"pres" => 1013.0, "hght" => 171, "tmpc" => 25.0, "dwpc" => 15.0, "drct" => 180, "sknt" => 10},
    %{"pres" => 925.0, "hght" => 800, "tmpc" => 20.0, "dwpc" => 12.0, "drct" => 200, "sknt" => 15},
    %{"pres" => 850.0, "hght" => 1500, "tmpc" => 15.0, "dwpc" => 8.0, "drct" => 220, "sknt" => 20}
  ]

  # Profile with a temperature inversion (temp increases with height in one layer)
  @inversion_profile [
    %{"pres" => 1013.0, "hght" => 100, "tmpc" => 25.0, "dwpc" => 15.0, "drct" => 180, "sknt" => 5},
    %{"pres" => 980.0, "hght" => 400, "tmpc" => 22.0, "dwpc" => 14.0, "drct" => 190, "sknt" => 8},
    %{"pres" => 950.0, "hght" => 700, "tmpc" => 26.0, "dwpc" => 10.0, "drct" => 200, "sknt" => 12},
    %{"pres" => 925.0, "hght" => 900, "tmpc" => 20.0, "dwpc" => 8.0, "drct" => 210, "sknt" => 15},
    %{"pres" => 850.0, "hght" => 1500, "tmpc" => 14.0, "dwpc" => 5.0, "drct" => 230, "sknt" => 20},
    %{"pres" => 700.0, "hght" => 3100, "tmpc" => 2.0, "dwpc" => -5.0, "drct" => 250, "sknt" => 30},
    %{"pres" => 500.0, "hght" => 5600, "tmpc" => -15.0, "dwpc" => -25.0, "drct" => 270, "sknt" => 40}
  ]

  # Profile designed to create a duct (M decreases with height)
  # Strong surface-based inversion with very dry air aloft → M decreases
  @ducting_profile [
    %{"pres" => 1013.0, "hght" => 0, "tmpc" => 30.0, "dwpc" => 25.0, "drct" => 180, "sknt" => 5},
    %{"pres" => 1000.0, "hght" => 100, "tmpc" => 35.0, "dwpc" => 5.0, "drct" => 180, "sknt" => 5},
    %{"pres" => 980.0, "hght" => 300, "tmpc" => 28.0, "dwpc" => 10.0, "drct" => 190, "sknt" => 8},
    %{"pres" => 950.0, "hght" => 550, "tmpc" => 24.0, "dwpc" => 12.0, "drct" => 200, "sknt" => 10},
    %{"pres" => 925.0, "hght" => 800, "tmpc" => 20.0, "dwpc" => 10.0, "drct" => 210, "sknt" => 12},
    %{"pres" => 850.0, "hght" => 1500, "tmpc" => 14.0, "dwpc" => 5.0, "drct" => 230, "sknt" => 18},
    %{"pres" => 700.0, "hght" => 3100, "tmpc" => 2.0, "dwpc" => -10.0, "drct" => 250, "sknt" => 25},
    %{"pres" => 500.0, "hght" => 5600, "tmpc" => -18.0, "dwpc" => -30.0, "drct" => 270, "sknt" => 35}
  ]

  describe "sat_vap_pres/1" do
    test "returns known value at 20°C (~23.4 hPa)" do
      result = SoundingParams.sat_vap_pres(20.0)
      assert_in_delta result, 23.4, 0.5
    end

    test "returns known value at 0°C (~6.1 hPa)" do
      result = SoundingParams.sat_vap_pres(0.0)
      assert_in_delta result, 6.1, 0.2
    end
  end

  describe "mixing_ratio/2" do
    test "returns reasonable value for typical surface conditions" do
      # At 15°C, 1013 hPa, mixing ratio ~10.7 g/kg
      result = SoundingParams.mixing_ratio(15.0, 1013.0)
      assert_in_delta result, 10.7, 1.0
    end
  end

  describe "derive/1" do
    test "returns nil for profiles with fewer than 3 valid levels" do
      short = [
        %{"pres" => 1013.0, "hght" => 171, "tmpc" => 25.0, "dwpc" => 15.0},
        %{"pres" => 925.0, "hght" => 800, "tmpc" => 20.0, "dwpc" => 12.0}
      ]

      assert SoundingParams.derive(short) == nil
    end

    test "returns nil for empty profile" do
      assert SoundingParams.derive([]) == nil
    end

    test "computes surface parameters" do
      result = SoundingParams.derive(@simple_profile)
      assert result
      assert_in_delta result.surface_temp_c, 25.0, 0.01
      assert_in_delta result.surface_dewpoint_c, 15.0, 0.01
      assert_in_delta result.surface_pressure_mb, 1013.0, 0.01
    end

    test "computes surface refractivity (N-units)" do
      result = SoundingParams.derive(@simple_profile)
      # At 25°C, 15°C dewpoint, 1013 hPa — N should be ~330-350
      assert result.surface_refractivity > 300
      assert result.surface_refractivity < 400
    end

    test "computes dN/dh gradients" do
      result = SoundingParams.derive(@simple_profile)
      # Standard atmosphere dN/dh is about -40/km
      assert result.min_refractivity_gradient < 0
    end

    test "detects temperature inversions" do
      result = SoundingParams.derive(@inversion_profile)
      assert result.inversions != []

      inv = hd(result.inversions)
      assert inv.strength > 0
      assert inv.base >= 0
    end

    test "no inversions in standard lapse profile" do
      # Simple profile has monotonic temperature decrease
      result = SoundingParams.derive(@simple_profile)
      assert result.inversions == []
    end

    test "computes precipitable water" do
      result = SoundingParams.derive(@inversion_profile)
      # PW should be positive and reasonable (5-80mm)
      assert result.precipitable_water_mm > 0
      assert result.precipitable_water_mm < 100
    end

    test "computes K-Index when standard levels available" do
      result = SoundingParams.derive(@inversion_profile)
      # K-Index should be computed (profile has 850, 700, 500 levels)
      assert result.k_index
    end

    test "computes Lifted Index when 500 hPa available" do
      result = SoundingParams.derive(@inversion_profile)
      assert result.lifted_index
    end

    test "detects ducting in strong inversion profile" do
      result = SoundingParams.derive(@ducting_profile)
      assert result.ducting_detected == true
      assert result.ducts != []
    end

    test "no ducting in standard profile" do
      result = SoundingParams.derive(@simple_profile)
      assert result.ducting_detected == false
      assert result.ducts == []
    end

    test "computes boundary layer depth" do
      result = SoundingParams.derive(@inversion_profile)
      # BL depth should be positive and reasonable
      assert result.boundary_layer_depth_m > 0
      assert result.boundary_layer_depth_m < 5000
    end

    test "returns level_count matching filtered profile" do
      result = SoundingParams.derive(@simple_profile)
      assert result.level_count == 3
    end

    test "filters out levels with nil pressure, temp, or height" do
      profile_with_nils = [
        %{"pres" => 1013.0, "hght" => 171, "tmpc" => 25.0, "dwpc" => 15.0, "drct" => 180, "sknt" => 10},
        %{"pres" => nil, "hght" => 500, "tmpc" => 22.0, "dwpc" => 13.0, "drct" => 190, "sknt" => 12},
        %{"pres" => 925.0, "hght" => 800, "tmpc" => 20.0, "dwpc" => 12.0, "drct" => 200, "sknt" => 15},
        %{"pres" => 850.0, "hght" => 1500, "tmpc" => 15.0, "dwpc" => 8.0, "drct" => 220, "sknt" => 20}
      ]

      result = SoundingParams.derive(profile_with_nils)
      assert result.level_count == 3
    end
  end
end
