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

  alias Microwaveprop.Weather.Grib2.ComplexPacking
  alias Microwaveprop.Weather.Grib2.LambertConformal
  alias Microwaveprop.Weather.Grib2.Section

  @fixture_path "test/fixtures/grib2/hrrr_tmp_2m.grib2"

  describe "extract_value/3 with real HRRR fixture" do
    @tag :slow
    test "extracts a temperature value within meteorological range" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        # First grid point (index 0)
        {:ok, value} =
          ComplexPacking.extract_value(parsed.packing_params, parsed.data, 0)

        # Should be a temperature in Kelvin (roughly 200-340K)
        assert value > 200.0 and value < 340.0,
               "Expected temperature 200-340K, got #{value}"
      else
        IO.puts("Skipping fixture test — #{@fixture_path} not found")
        :ok
      end
    end

    @tag :slow
    test "extracts values at multiple indices" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        # Check several indices across the grid
        for idx <- [0, 1000, 100_000, 500_000, 1_000_000, 1_905_140] do
          {:ok, value} =
            ComplexPacking.extract_value(parsed.packing_params, parsed.data, idx)

          assert value > 200.0 and value < 340.0,
                 "Index #{idx}: expected 200-340K, got #{value}"
        end
      else
        :ok
      end
    end

    @tag :slow
    test "Dallas area temperature is reasonable for a US city" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        grid = parsed.grid_params
        {:ok, {i, j}} = LambertConformal.to_grid_index(grid, 32.78, -96.8)
        index = j * grid.nx + i

        {:ok, value} =
          ComplexPacking.extract_value(parsed.packing_params, parsed.data, index)

        # Dallas in March — Kelvin should be roughly 270-310K (roughly -3C to 37C)
        temp_c = value - 273.15

        assert temp_c > -10.0 and temp_c < 45.0,
               "Dallas temp #{Float.round(temp_c, 1)}C seems unreasonable"
      else
        :ok
      end
    end
  end

  describe "decode_all/2" do
    @tag :slow
    test "decodes all values from fixture" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        {:ok, values_arr} = ComplexPacking.decode_all(parsed.packing_params, parsed.data)

        assert :array.size(values_arr) == parsed.packing_params.num_data_points

        # Check a sample of values are in reasonable temperature range
        for idx <- [0, 100_000, 500_000, 1_000_000, 1_905_140] do
          val = :array.get(idx, values_arr)

          assert val > 200.0 and val < 340.0,
                 "Index #{idx}: expected 200-340K, got #{val}"
        end
      else
        :ok
      end
    end
  end

  describe "extract_values/3" do
    @tag :slow
    test "batch extracts multiple values matching individual calls" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        indices = [0, 1000, 100_000, 500_000, 1_000_000]

        {:ok, batch_result} =
          ComplexPacking.extract_values(parsed.packing_params, parsed.data, indices)

        assert map_size(batch_result) == length(indices)

        # Each batch value should match individual extraction
        for idx <- indices do
          {:ok, individual} =
            ComplexPacking.extract_value(parsed.packing_params, parsed.data, idx)

          assert_in_delta batch_result[idx],
                          individual,
                          0.001,
                          "Index #{idx}: batch #{batch_result[idx]} != individual #{individual}"
        end
      else
        :ok
      end
    end

    @tag :slow
    test "returns values in meteorological range" do
      if File.exists?(@fixture_path) do
        fixture = File.read!(@fixture_path)
        {:ok, parsed} = Section.parse_message(fixture)

        indices = [0, 500_000, 1_905_140]
        {:ok, result} = ComplexPacking.extract_values(parsed.packing_params, parsed.data, indices)

        for idx <- indices do
          assert result[idx] > 200.0 and result[idx] < 340.0,
                 "Index #{idx}: expected 200-340K, got #{result[idx]}"
        end
      else
        :ok
      end
    end
  end
end
