defmodule Microwaveprop.RoverTest do
  use Microwaveprop.DataCase, async: true

  import Microwaveprop.AccountsFixtures

  alias Microwaveprop.Rover
  alias Microwaveprop.Rover.FixedStation

  describe "list_stations/1" do
    test "returns user's stations ordered by position asc" do
      user = user_fixture()
      {:ok, _b} = Rover.create_station(user, %{callsign: "B5BBB", lat: 33.0, lon: -96.0})
      {:ok, _a} = Rover.create_station(user, %{callsign: "A5AAA", lat: 33.0, lon: -96.0})

      stations = Rover.list_stations(user)

      assert Enum.map(stations, & &1.callsign) == ["B5BBB", "A5AAA"]
      assert Enum.map(stations, & &1.position) == [0, 1]
    end
  end

  describe "create_station/2" do
    test "derives lat/lon from grid only" do
      user = user_fixture()
      {:ok, station} = Rover.create_station(user, %{callsign: "W5LUA", grid: "EM13qc"})
      assert_in_delta station.lat, 33.10, 0.05
      assert_in_delta station.lon, -96.625, 0.05
    end

    test "errors with neither grid nor lat/lon" do
      user = user_fixture()
      assert {:error, %Ecto.Changeset{}} = Rover.create_station(user, %{callsign: "W5LUA"})
    end

    test "unique constraint surfaces as changeset error" do
      user = user_fixture()
      {:ok, _} = Rover.create_station(user, %{callsign: "W5LUA", lat: 33.0, lon: -96.0})

      assert {:error, %Ecto.Changeset{}} =
               Rover.create_station(user, %{callsign: "W5LUA", lat: 34.0, lon: -97.0})
    end
  end

  describe "update_station/3" do
    test "foreign user returns :not_found" do
      user = user_fixture()
      other = user_fixture()
      {:ok, station} = Rover.create_station(user, %{callsign: "W5LUA", lat: 33.0, lon: -96.0})

      assert {:error, :not_found} = Rover.update_station(other, station.id, %{selected: false})
    end
  end

  describe "delete_station/2" do
    test "foreign user returns :not_found" do
      user = user_fixture()
      other = user_fixture()
      {:ok, station} = Rover.create_station(user, %{callsign: "W5LUA", lat: 33.0, lon: -96.0})

      assert {:error, :not_found} = Rover.delete_station(other, station.id)
    end

    test "owner deletes" do
      user = user_fixture()
      {:ok, station} = Rover.create_station(user, %{callsign: "W5LUA", lat: 33.0, lon: -96.0})
      assert {:ok, %FixedStation{}} = Rover.delete_station(user, station.id)
      assert Rover.list_stations(user) == []
    end
  end

  describe "toggle_selected/2" do
    test "flips boolean" do
      user = user_fixture()
      {:ok, station} = Rover.create_station(user, %{callsign: "W5LUA", lat: 33.0, lon: -96.0})
      assert station.selected == true
      assert {:ok, updated} = Rover.toggle_selected(user, station.id)
      assert updated.selected == false
      assert {:ok, again} = Rover.toggle_selected(user, station.id)
      assert again.selected == true
    end
  end

  describe "default_stations/0" do
    test "returns 3 hardcoded NTMS stations" do
      stations = Rover.default_stations()
      assert length(stations) == 3
      callsigns = Enum.map(stations, & &1.callsign)
      assert "W5LUA" in callsigns
      assert "W5HN" in callsigns
      assert "N5XU" in callsigns
      assert Enum.all?(stations, & &1.selected)
      assert Enum.map(stations, & &1.position) == [0, 1, 2]

      Enum.each(stations, fn s ->
        assert is_float(s.lat)
        assert is_float(s.lon)
      end)
    end
  end
end
