defmodule MicrowavepropWeb.SkewTTest do
  use ExUnit.Case, async: true

  alias MicrowavepropWeb.SkewT

  describe "saturation_vapor_pressure/1 (Magnus)" do
    test "reference values are within 1% of textbook" do
      # At 0°C the Magnus formula gives ≈ 6.112 hPa.
      assert_in_delta SkewT.saturation_vapor_pressure(0.0), 6.112, 0.05

      # At 20°C es ≈ 23.37 hPa.
      assert_in_delta SkewT.saturation_vapor_pressure(20.0), 23.37, 0.2

      # At -20°C es ≈ 1.254 hPa.
      assert_in_delta SkewT.saturation_vapor_pressure(-20.0), 1.254, 0.05
    end
  end

  describe "temperature_from_mixing_ratio/2" do
    test "round-trips mixing ratios" do
      # Pick a pressure and temperature, compute ws, invert, expect the original.
      p = 850.0

      for t <- [-10.0, 0.0, 15.0, 25.0] do
        es = SkewT.saturation_vapor_pressure(t)
        ws_g_kg = 622.0 * es / (p - es)
        recovered = SkewT.temperature_from_mixing_ratio(ws_g_kg, p)
        assert_in_delta recovered, t, 0.05
      end
    end
  end

  describe "dry_adiabat_temperature/2" do
    test "θ == T at 1000 mb" do
      # At the reference pressure the potential temperature equals the
      # actual temperature — so θ = 300 K ⇒ T(1000) = 300 - 273.15 ≈ 26.85°C.
      assert_in_delta SkewT.dry_adiabat_temperature(300.0, 1000.0), 26.85, 0.02
    end

    test "a parcel lifted dry-adiabatically cools" do
      # Lifting from 1000 mb (θ = 290 K, T ≈ 16.85°C) to 700 mb should
      # drop the temperature by ~26°C (roughly 9.8°C/km, ~3 km).
      t_surface = SkewT.dry_adiabat_temperature(290.0, 1000.0)
      t_700 = SkewT.dry_adiabat_temperature(290.0, 700.0)
      assert t_surface - t_700 > 20.0
    end
  end

  describe "project/3" do
    setup do
      chart = SkewT.chart_config(width: 400, height: 500)
      %{chart: chart}
    end

    test "p_bot lands at the chart bottom and p_top at the top", %{chart: chart} do
      {_x_bot, y_bot} = SkewT.project(0.0, chart.p_bot, chart)
      {_x_top, y_top} = SkewT.project(0.0, chart.p_top, chart)

      # SVG y grows downward, so p_bot (bottom) has a larger y than p_top.
      assert y_bot > y_top
      assert_in_delta y_bot, chart.padding_top + chart.plot_height, 0.5
      assert_in_delta y_top, chart.padding_top, 0.5
    end

    test "isotherms skew right as pressure decreases", %{chart: chart} do
      # Same temperature at two pressures should have a larger x at
      # the lower pressure (top of chart) because of the skew.
      {x_bot, _} = SkewT.project(0.0, chart.p_bot, chart)
      {x_top, _} = SkewT.project(0.0, chart.p_top, chart)
      assert x_top > x_bot
    end

    test "temperature range spans the bottom of the chart", %{chart: chart} do
      # At p_bot, t_min lands at the left edge of the data area.
      {x_min, _} = SkewT.project(chart.t_min, chart.p_bot, chart)
      {x_max, _} = SkewT.project(chart.t_max, chart.p_bot, chart)
      assert_in_delta x_min, chart.padding_left, 0.5
      assert_in_delta x_max, chart.padding_left + chart.plot_width, 0.5
    end
  end

  describe "build/2" do
    test "with no profile returns nil" do
      assert SkewT.build(nil) == nil
      assert SkewT.build([]) == nil
    end

    test "returns a map of rendering primitives for a valid profile" do
      profile = [
        %{"pres" => 1000.0, "hght" => 100.0, "tmpc" => 25.0, "dwpc" => 18.0},
        %{"pres" => 850.0, "hght" => 1500.0, "tmpc" => 15.0, "dwpc" => 10.0},
        %{"pres" => 700.0, "hght" => 3100.0, "tmpc" => 5.0, "dwpc" => -2.0},
        %{"pres" => 500.0, "hght" => 5600.0, "tmpc" => -15.0, "dwpc" => -25.0}
      ]

      chart = SkewT.build(profile)

      assert is_map(chart)
      assert chart.width > 0
      assert chart.height > 0
      # Background grid
      assert chart.isobars != []
      assert chart.isotherms != []
      assert chart.dry_adiabats != []
      assert chart.mixing_ratio_lines != []
      # Traces
      assert chart.t_points != []
      assert chart.td_points != []
      # The first plotted t_point should correspond to the highest pressure
      # (surface-most) profile entry.
      assert length(chart.t_points) == 4
    end

    test "filters out profile levels with missing temperature or dewpoint independently" do
      profile = [
        %{"pres" => 1000.0, "tmpc" => 25.0, "dwpc" => 18.0},
        %{"pres" => 900.0, "tmpc" => nil, "dwpc" => 15.0},
        %{"pres" => 850.0, "tmpc" => 15.0, "dwpc" => nil},
        %{"pres" => 700.0, "tmpc" => 5.0, "dwpc" => -2.0}
      ]

      chart = SkewT.build(profile)
      # 3 levels have tmpc, 3 have dwpc — the traces are independent.
      assert length(chart.t_points) == 3
      assert length(chart.td_points) == 3
    end
  end
end
