defmodule MicrowavepropWeb.SkewTPropertyTest do
  @moduledoc """
  StreamData property tests for `MicrowavepropWeb.SkewT`'s atmospheric
  math helpers.

  Invariants covered:

  * Magnus saturation vapor pressure is positive and strictly
    monotonic in temperature.
  * `temperature_from_mixing_ratio/2` is the inverse of the
    ws = 622·es / (p − es) forward relation.
  * Along a dry adiabat, temperature decreases monotonically as
    pressure falls (lift a parcel → it cools).
  * At the 1000 mb reference pressure, dry-adiabat T equals θ − 273.15
    (definition of potential temperature).
  * `project/3` reproduces the chart corners and preserves pressure
    ordering on the y-axis.
  """
  use ExUnit.Case, async: true
  use ExUnitProperties

  alias MicrowavepropWeb.SkewT

  describe "saturation_vapor_pressure/1" do
    property "is strictly positive across the troposphere" do
      check all(t_c <- float(min: -80.0, max: 50.0)) do
        assert SkewT.saturation_vapor_pressure(t_c) > 0
      end
    end

    property "is strictly monotonic in temperature" do
      check all(
              t_c <- float(min: -80.0, max: 50.0),
              delta <- float(min: 0.001, max: 10.0)
            ) do
        lo = SkewT.saturation_vapor_pressure(t_c)
        hi = SkewT.saturation_vapor_pressure(t_c + delta)
        assert hi > lo
      end
    end
  end

  describe "temperature_from_mixing_ratio/2" do
    property "round-trips T → ws → T for realistic (T, p) pairs" do
      check all(
              t_c <- float(min: -30.0, max: 35.0),
              p_mb <- float(min: 300.0, max: 1050.0)
            ) do
        es = SkewT.saturation_vapor_pressure(t_c)

        # Only test where ws is well-defined (p > es).
        if p_mb > es + 1.0 do
          ws_g_kg = 622.0 * es / (p_mb - es)
          recovered = SkewT.temperature_from_mixing_ratio(ws_g_kg, p_mb)
          assert_in_delta recovered, t_c, 0.01
        end
      end
    end
  end

  describe "dry_adiabat_temperature/2" do
    property "equals θ − 273.15 at the 1000 mb reference pressure" do
      check all(theta_k <- float(min: 200.0, max: 400.0)) do
        t_c = SkewT.dry_adiabat_temperature(theta_k, 1000.0)
        assert_in_delta t_c, theta_k - 273.15, 1.0e-6
      end
    end

    property "temperature decreases as pressure decreases along an adiabat" do
      check all(
              theta_k <- float(min: 250.0, max: 360.0),
              p_hi <- float(min: 500.0, max: 1050.0),
              drop <- float(min: 10.0, max: 400.0)
            ) do
        p_lo = p_hi - drop

        if p_lo > 50.0 do
          t_hi = SkewT.dry_adiabat_temperature(theta_k, p_hi)
          t_lo = SkewT.dry_adiabat_temperature(theta_k, p_lo)
          assert t_hi > t_lo
        end
      end
    end
  end

  describe "project/3" do
    setup do
      %{chart: SkewT.chart_config([])}
    end

    property "projects p_bot to the plot bottom and p_top to the plot top",
             %{chart: chart} do
      check all(t_c <- float(min: chart.t_min, max: chart.t_max)) do
        {_x_bot, y_bot} = SkewT.project(t_c, chart.p_bot, chart)
        {_x_top, y_top} = SkewT.project(t_c, chart.p_top, chart)

        # SVG y grows downward: the bottom of the chart has a larger y.
        assert y_bot > y_top
        assert_in_delta y_bot, chart.padding_top + chart.plot_height, 1.0e-6
        assert_in_delta y_top, chart.padding_top, 1.0e-6
      end
    end

    property "pressure ordering is inverted on the y-axis (higher p → larger y)",
             %{chart: chart} do
      check all(
              p_hi <- float(min: 200.0, max: 1050.0),
              p_lo <- float(min: 100.0, max: 1000.0),
              t_c <- float(min: -20.0, max: 20.0)
            ) do
        if p_hi > p_lo + 1.0 do
          {_x1, y_hi} = SkewT.project(t_c, p_hi, chart)
          {_x2, y_lo} = SkewT.project(t_c, p_lo, chart)
          assert y_hi > y_lo
        end
      end
    end
  end
end
