defmodule MicrowavepropWeb.ScoresControllerTest do
  use MicrowavepropWeb.ConnCase, async: false

  alias Microwaveprop.Propagation.ScoreCache
  alias Microwaveprop.Propagation.ScoresFile

  setup do
    ScoreCache.clear()

    on_exit(fn ->
      ScoreCache.clear()
      ScoresFile.prune_older_than(~U[2099-12-31 23:59:59Z])
    end)

    :ok
  end

  describe "GET /scores/cells" do
    test "returns 400 for missing or invalid params", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?band=nope")
      assert json_response(conn, 400)
    end

    test "returns the binary score pack for the band's earliest valid_time when ?time is absent",
         %{conn: conn} do
      valid_time = ~U[2026-04-28 12:00:00Z]
      ScoresFile.write!(10_000, valid_time, [%{lat: 33.0, lon: -97.0, score: 75}])

      conn =
        get(
          conn,
          ~p"/scores/cells?band=10000&south=32.5&north=33.5&west=-97.5&east=-96.5"
        )

      assert ["application/octet-stream"] = get_resp_header(conn, "content-type")
      body = response(conn, 200)

      assert <<"PSCR", 1::8, _reserved::24, 1::little-32, rest::binary>> = body

      <<lat::little-float-32, lon::little-float-32, score::8, _rest::binary>> = rest
      assert_in_delta lat, 33.0, 0.001
      assert_in_delta lon, -97.0, 0.001
      assert score == 75
    end

    test "with ?time returns scores for that valid_time", %{conn: conn} do
      analysis = ~U[2026-04-28 12:00:00Z]
      forecast = ~U[2026-04-28 13:00:00Z]

      ScoresFile.write!(10_000, analysis, [%{lat: 33.0, lon: -97.0, score: 50}])
      ScoresFile.write!(10_000, forecast, [%{lat: 33.0, lon: -97.0, score: 88}])

      time = DateTime.to_iso8601(forecast)

      conn =
        get(
          conn,
          ~p"/scores/cells?band=10000&time=#{time}&south=32.5&north=33.5&west=-97.5&east=-96.5"
        )

      body = response(conn, 200)
      assert <<"PSCR", 1::8, _reserved::24, 1::little-32, rest::binary>> = body
      <<_lat::little-float-32, _lon::little-float-32, score::8, _::binary>> = rest
      assert score == 88
    end

    test "returns an empty pack when no data exists for the band", %{conn: conn} do
      conn =
        get(
          conn,
          ~p"/scores/cells?band=999999&south=32.0&north=34.0&west=-98.0&east=-96.0"
        )

      assert ["application/octet-stream"] = get_resp_header(conn, "content-type")
      body = response(conn, 200)
      assert <<"PSCR", 1::8, _reserved::24, 0::little-32>> = body
    end

    test "returns 400 for bad ?time format", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?band=10000&time=not-a-time&south=0&north=1&west=-1&east=0")
      assert json_response(conn, 400) == %{"error" => "invalid params"}
    end

    test "returns 400 for bad float values in bounds", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?band=10000&south=abc&north=1&west=-1&east=0")
      assert json_response(conn, 400) == %{"error" => "invalid params"}
    end

    test "returns 400 when band param is not an integer", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?band=abc&south=0&north=1&west=-1&east=0")
      assert json_response(conn, 400) == %{"error" => "invalid params"}
    end

    test "returns 400 when band param is missing", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?south=0&north=1&west=-1&east=0")
      assert json_response(conn, 400) == %{"error" => "invalid params"}
    end

    test "returns 400 when time param is an empty string", %{conn: conn} do
      conn = get(conn, ~p"/scores/cells?band=10000&time=&south=0&north=1&west=-1&east=0")
      assert json_response(conn, 400) == %{"error" => "invalid params"}
    end
  end
end
