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

  alias Microwaveprop.Weather.SolarClient

  @sample_gfz_file """
  # Comment line 1
  # Comment line 2
  # YYYY MM DD  days days_m  Bsr dB  Kp1  Kp2  Kp3  Kp4  Kp5  Kp6  Kp7  Kp8  ap1  ap2  ap3  ap4  ap5  ap6  ap7  ap8  Ap SN F10.7obs F10.7adj D
  2024 06 15 33769 33769.5 2589 18  2.000  1.667  2.333  3.000  2.667  1.333  1.000  1.667   7   6   9  15  12   5   4   6     8 120   150.2   148.5 2
  2024 06 16 33770 33770.5 2589 19  3.333  4.000  2.667  1.000  0.667  1.333  2.000  1.667  18  27  12   4   3   5   7   6    10 115   145.0   143.2 2
  """

  @sample_row_with_missing "1932 01 01     0     0.5 1352 10  3.333  2.667  2.333  2.667  3.333  2.667  3.333  3.333   18   12    9   12   18   12   18   18    15  22     -1.0     -1.0 2"

  @sample_row_all_missing "1940 05 01  3043  3043.5 1385 23 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000 -1.000   -1   -1   -1   -1   -1   -1   -1   -1    -1  -1     -1.0     -1.0 0"

  describe "parse_gfz_row/1" do
    test "parses a complete data row" do
      row =
        "2024 06 15 33769 33769.5 2589 18  2.000  1.667  2.333  3.000  2.667  1.333  1.000  1.667   7   6   9  15  12   5   4   6     8 120   150.2   148.5 2"

      assert {:ok, parsed} = SolarClient.parse_gfz_row(row)
      assert parsed.date == ~D[2024-06-15]
      assert parsed.sfi == 150.2
      assert parsed.sfi_adjusted == 148.5
      assert parsed.sunspot_number == 120
      assert parsed.ap_index == 8
      assert parsed.kp_values == [2.0, 1.667, 2.333, 3.0, 2.667, 1.333, 1.0, 1.667]
    end

    test "maps -1 values to nil for F10.7" do
      assert {:ok, parsed} = SolarClient.parse_gfz_row(@sample_row_with_missing)
      assert parsed.date == ~D[1932-01-01]
      assert is_nil(parsed.sfi)
      assert is_nil(parsed.sfi_adjusted)
      assert parsed.sunspot_number == 22
      assert parsed.ap_index == 15
    end

    test "maps all -1 values to nil" do
      assert {:ok, parsed} = SolarClient.parse_gfz_row(@sample_row_all_missing)
      assert parsed.date == ~D[1940-05-01]
      assert is_nil(parsed.sfi)
      assert is_nil(parsed.sfi_adjusted)
      assert is_nil(parsed.sunspot_number)
      assert is_nil(parsed.ap_index)
      assert parsed.kp_values == [nil, nil, nil, nil, nil, nil, nil, nil]
    end
  end

  describe "parse_gfz_file/1" do
    test "skips comment lines and parses data rows" do
      result = SolarClient.parse_gfz_file(@sample_gfz_file)
      assert length(result) == 2

      [first, second] = result
      assert first.date == ~D[2024-06-15]
      assert first.sfi == 150.2
      assert second.date == ~D[2024-06-16]
      assert second.sfi == 145.0
    end

    test "returns empty list for empty input" do
      assert SolarClient.parse_gfz_file("") == []
    end

    test "returns empty list for comments-only input" do
      assert SolarClient.parse_gfz_file("# just a comment\n# another") == []
    end
  end

  describe "fetch_solar_indices_since/1" do
    test "filters records to those on or after the given date" do
      records = SolarClient.parse_gfz_file(@sample_gfz_file)
      # Records are 2024-06-15 and 2024-06-16
      filtered = SolarClient.filter_since(records, ~D[2024-06-16])
      assert length(filtered) == 1
      assert hd(filtered).date == ~D[2024-06-16]
    end

    test "returns all records when since_date is before all records" do
      records = SolarClient.parse_gfz_file(@sample_gfz_file)
      filtered = SolarClient.filter_since(records, ~D[2020-01-01])
      assert length(filtered) == 2
    end

    test "returns empty list when since_date is after all records" do
      records = SolarClient.parse_gfz_file(@sample_gfz_file)
      filtered = SolarClient.filter_since(records, ~D[2025-01-01])
      assert filtered == []
    end
  end

  describe "data_url/0" do
    test "returns the GFZ data URL" do
      assert SolarClient.data_url() == "https://kp.gfz.de/app/files/Kp_ap_Ap_SN_F107_since_1932.txt"
    end
  end
end
