defmodule Microwaveprop.Workers.TerrainProfileWorkerTest do
  use Microwaveprop.DataCase, async: true

  alias Microwaveprop.Radio.Contact
  alias Microwaveprop.Terrain
  alias Microwaveprop.Terrain.ElevationClient
  alias Microwaveprop.Workers.TerrainProfileWorker

  @qso_attrs %{
    station1: "W5XD",
    station2: "K5TR",
    qso_timestamp: ~U[2026-03-28 18:00:00Z],
    mode: "CW",
    band: Decimal.new("1296"),
    pos1: %{"lat" => 32.9, "lon" => -97.0},
    pos2: %{"lat" => 30.3, "lon" => -97.7},
    distance_km: Decimal.new("295")
  }

  defp create_contact(attrs \\ %{}) do
    {:ok, contact} =
      %Contact{}
      |> Contact.changeset(Map.merge(@qso_attrs, attrs))
      |> Repo.insert()

    contact
  end

  defp stub_elevation_api do
    Req.Test.stub(ElevationClient, fn conn ->
      params = Plug.Conn.fetch_query_params(conn).query_params
      lat_count = params["latitude"] |> String.split(",") |> length()
      elevations = List.duplicate(200.0, lat_count)
      Req.Test.json(conn, %{"elevation" => elevations})
    end)
  end

  describe "perform/1" do
    test "fetches elevation and stores terrain profile" do
      stub_elevation_api()
      contact = create_contact()

      refute Terrain.has_terrain_profile?(contact.id)

      job = TerrainProfileWorker.new(%{"contact_id" => contact.id})
      assert :ok = TerrainProfileWorker.perform(%Oban.Job{args: job.changes.args})

      assert Terrain.has_terrain_profile?(contact.id)
      profile = Terrain.get_terrain_profile(contact.id)
      assert profile.sample_count == 65
      assert profile.verdict in ["CLEAR", "FRESNEL_MINOR", "FRESNEL_PARTIAL", "BLOCKED"]
      assert is_list(profile.path_points)
    end

    test "skips if terrain profile already exists" do
      stub_elevation_api()
      contact = create_contact()

      {:ok, _} =
        Terrain.upsert_terrain_profile(%{
          contact_id: contact.id,
          sample_count: 65,
          path_points: [],
          verdict: "CLEAR"
        })

      job = TerrainProfileWorker.new(%{"contact_id" => contact.id})
      assert :ok = TerrainProfileWorker.perform(%Oban.Job{args: job.changes.args})
    end

    test "returns error when elevation API fails" do
      Req.Test.stub(ElevationClient, fn conn ->
        Plug.Conn.send_resp(conn, 500, "error")
      end)

      contact = create_contact()

      job = TerrainProfileWorker.new(%{"contact_id" => contact.id})
      assert {:error, _} = TerrainProfileWorker.perform(%Oban.Job{args: job.changes.args})

      refute Terrain.has_terrain_profile?(contact.id)
    end

    test "uses correct frequency from band" do
      stub_elevation_api()
      contact = create_contact(%{band: Decimal.new("10368")})

      job = TerrainProfileWorker.new(%{"contact_id" => contact.id})
      assert :ok = TerrainProfileWorker.perform(%Oban.Job{args: job.changes.args})

      profile = Terrain.get_terrain_profile(contact.id)
      assert profile.verdict
    end
  end
end
