defmodule Aprsme.PacketsFixtures do
  @moduledoc """
  This module defines test helpers for creating
  entities via the `Aprsme.Packets` context.
  """

  alias Aprsme.Callsign
  alias Aprsme.Packet
  alias Aprsme.Repo

  @doc """
  Generate a packet.
  """
  def packet_fixture(attrs \\ %{}) do
    base_attrs = %{
      sender: "TEST-1",
      base_callsign: "TEST",
      ssid: "1",
      destination: "APRS",
      received_at: DateTime.utc_now(),
      lat: Decimal.new("40.7128"),
      lon: Decimal.new("-74.0060"),
      has_position: true,
      raw_packet: "TEST-1>APRS:=4042.77N/07400.36W>Test packet",
      data_type: "position",
      path: "APRS",
      data_extended: %{
        symbol_table_id: "/",
        symbol_code: ">",
        comment: "Test packet"
      }
    }

    # Extract base_callsign and ssid from sender if provided
    final_attrs =
      case Map.get(attrs, :sender) do
        nil ->
          base_attrs

        sender ->
          {base, ssid} = Callsign.extract_parts(sender)

          Map.merge(base_attrs, %{base_callsign: base, ssid: ssid})
      end

    # Ensure location is set if lat/lon are provided
    final_attrs_with_location =
      if final_attrs[:lat] && final_attrs[:lon] do
        location = %Geo.Point{
          coordinates: {Decimal.to_float(final_attrs[:lon]), Decimal.to_float(final_attrs[:lat])},
          srid: 4326
        }

        Map.put(final_attrs, :location, location)
      else
        final_attrs
      end

    {:ok, packet} =
      attrs
      |> Enum.into(final_attrs_with_location)
      |> then(&Packet.changeset(%Packet{}, &1))
      |> Repo.insert()

    packet
  end

  @doc """
  Batch-insert multiple packet fixtures in a single Repo.insert_all call.
  Useful for tests that need many packets without the overhead of individual
  changeset + insert round trips.
  """
  def batch_packet_fixtures(count, base_attrs \\ %{}) do
    base_attrs = Map.new(base_attrs)
    now = DateTime.truncate(DateTime.utc_now(), :second)

    lat_num = base_attrs[:lat] || 40.7
    lon_num = base_attrs[:lon] || -74.0

    lat_str = "#{lat_num}"
    lon_str = "#{lon_num}"

    location = %Geo.Point{
      coordinates: {lon_num, lat_num},
      srid: 4326
    }

    attrs_list =
      for i <- 1..count do
        sender = Map.get(base_attrs, :sender, "TEST#{i}")
        {base, ssid} = Callsign.extract_parts(sender)

        received_at =
          DateTime.truncate(Map.get(base_attrs, :received_at) || DateTime.add(now, -rem(i, 60) * 60, :second), :second)

        %{
          sender: sender,
          base_callsign: base,
          ssid: ssid,
          destination: Map.get(base_attrs, :destination, "APRS"),
          received_at: received_at,
          lat: Decimal.new(lat_str),
          lon: Decimal.new(lon_str),
          location: location,
          has_position: true,
          raw_packet: Map.get(base_attrs, :raw_packet, "#{sender}>APRS:=4042.77N/07400.36W>Test packet"),
          data_type: Map.get(base_attrs, :data_type, "position"),
          path: Map.get(base_attrs, :path, "APRS"),
          comment: Map.get(base_attrs, :comment, "Test station #{i}"),
          symbol_code: ">",
          symbol_table_id: "/",
          inserted_at: now,
          updated_at: now
        }
      end

    Repo.insert_all(Packet, attrs_list)
    count
  end
end
