defmodule Aprsme.PacketsMicETest do
  use Aprsme.DataCase

  alias Aprs.Types.MicE
  alias Aprsme.Packets

  describe "store_packet/1 with MicE data" do
    test "stores packet with MicE data_extended successfully" do
      # This test reproduces the exact error scenario from the bug report
      mic_e_data = %MicE{
        lat_degrees: 49,
        lat_minutes: 14,
        lat_fractional: 72,
        lat_direction: :north,
        lon_direction: :east,
        longitude_offset: 100,
        message_code: "M02",
        message_description: "In Service",
        dti: "`",
        heading: 0,
        lon_degrees: 12,
        lon_minutes: 5,
        lon_fractional: 75,
        speed: 0,
        manufacturer: "Kenwood TH-D74A",
        message: ">Harald QRV R1298,625"
      }

      packet_data = %{
        base_callsign: "DG1ID",
        ssid: "9",
        sender: "DG1ID-9",
        destination: "APRS",
        data_type: "mic_e",
        path: "TCPIP*",
        information_field: "MicE packet data",
        data_extended: mic_e_data
      }

      # This should not raise a KeyError
      assert {:ok, stored_packet} = Packets.store_packet(packet_data)

      # Verify the packet was stored with correct position data
      assert stored_packet.sender == "DG1ID-9"
      assert stored_packet.has_position == true

      # Verify coordinates were calculated correctly from MicE components
      expected_lat = 49.0 + 14.0 / 60.0
      expected_lon = 12.0 + 5.0 / 60.0

      assert stored_packet.lat |> Decimal.to_float() |> Float.round(3) == Float.round(expected_lat, 3)
      assert stored_packet.lon |> Decimal.to_float() |> Float.round(3) == Float.round(expected_lon, 3)
    end

    test "handles MicE data with south/west coordinates" do
      mic_e_data = %MicE{
        lat_degrees: 34,
        lat_minutes: 30,
        lat_fractional: 0,
        lat_direction: :south,
        lon_direction: :west,
        longitude_offset: 100,
        message_code: "M01",
        message_description: "En Route",
        dti: "`",
        heading: 90,
        lon_degrees: 118,
        lon_minutes: 15,
        lon_fractional: 30,
        speed: 25,
        manufacturer: "Kenwood TH-D74A",
        message: "Test message"
      }

      packet_data = %{
        base_callsign: "TEST",
        ssid: "1",
        sender: "TEST-1",
        destination: "APRS",
        data_type: "mic_e",
        path: "WIDE1-1,WIDE2-2",
        information_field: "MicE test packet",
        data_extended: mic_e_data
      }

      assert {:ok, stored_packet} = Packets.store_packet(packet_data)

      # Verify south latitude is negative
      expected_lat = -(34.0 + 30.0 / 60.0)
      # Verify west longitude is negative
      expected_lon = -(118.0 + 15.0 / 60.0)

      assert stored_packet.lat |> Decimal.to_float() |> Float.round(3) == Float.round(expected_lat, 3)
      assert stored_packet.lon |> Decimal.to_float() |> Float.round(3) == Float.round(expected_lon, 3)
      assert Decimal.to_float(stored_packet.lat) < 0
      assert Decimal.to_float(stored_packet.lon) < 0
    end

    test "handles MicE data with missing position components gracefully" do
      # Test with incomplete MicE data (missing some coordinate components)
      incomplete_mic_e = %MicE{
        lat_degrees: nil,
        lat_minutes: 14,
        lat_fractional: 72,
        lat_direction: :north,
        lon_direction: :east,
        longitude_offset: 100,
        message_code: "M02",
        message_description: "In Service",
        dti: "`",
        heading: 0,
        lon_degrees: 12,
        lon_minutes: 5,
        lon_fractional: 75,
        speed: 0,
        manufacturer: "Kenwood TH-D74A",
        message: "Test incomplete"
      }

      packet_data = %{
        base_callsign: "TEST",
        ssid: "2",
        sender: "TEST-2",
        destination: "APRS",
        data_type: "mic_e",
        path: "TCPIP*",
        information_field: "Incomplete MicE",
        data_extended: incomplete_mic_e
      }

      # Should still store the packet, but without position data
      assert {:ok, stored_packet} = Packets.store_packet(packet_data)
      assert stored_packet.sender == "TEST-2"
      # Should not have position data when components are missing
      assert stored_packet.has_position != true || stored_packet.has_position == nil
    end

    test "MicE Access behavior works correctly" do
      # Test the Access behavior implementation directly
      mic_e = %MicE{
        lat_degrees: 40,
        lat_minutes: 30,
        lat_fractional: 0,
        lat_direction: :north,
        lon_degrees: 74,
        lon_minutes: 15,
        lon_fractional: 0,
        lon_direction: :west
      }

      # Test bracket notation (which triggers Access behavior)
      assert mic_e[:latitude] == 40.5
      assert mic_e[:longitude] == -74.25

      # Test that we can access both calculated and direct fields
      assert mic_e[:lat_degrees] == 40
      assert mic_e[:message_code] == nil

      # Test get_in works
      assert get_in(mic_e, [:latitude]) == 40.5
      assert get_in(mic_e, [:longitude]) == -74.25
    end

    test "exact error case from bug report - DG1ID-9" do
      # This test reproduces the EXACT error scenario from the bug report
      # Exception in store_packet for "DG1ID-9": %KeyError{key: :latitude, term: %Parser.Types.MicE{...
      mic_e_data = %MicE{
        lat_degrees: 49,
        lat_minutes: 14,
        lat_fractional: 72,
        lat_direction: :north,
        lon_direction: :east,
        longitude_offset: 100,
        message_code: "M02",
        message_description: "In Service",
        dti: "`",
        heading: 0,
        # Note: this is the exact value from the error
        lon_degrees: 198,
        lon_minutes: 5,
        lon_fractional: 75,
        speed: 0,
        manufacturer: "Kenwood TH-D74A",
        message: ">Harald QRV R1298,625"
      }

      packet_data = %{
        base_callsign: "DG1ID",
        ssid: "9",
        sender: "DG1ID-9",
        destination: "APRS",
        data_type: "mic_e",
        path: "TCPIP*",
        information_field: "MicE packet from error report",
        data_extended: mic_e_data
      }

      # This should NOT raise a KeyError{key: :latitude} anymore
      # The fix ensures we use data_extended[:latitude] instead of data_extended.latitude
      assert {:ok, stored_packet} = Packets.store_packet(packet_data)

      # Verify the packet was stored correctly
      assert stored_packet.sender == "DG1ID-9"

      # Note: longitude 198 is invalid (outside -180 to 180 range)
      # So the packet should be stored but without position data
      # This is correct behavior - the important thing is no KeyError was raised
      assert stored_packet.has_position != true || stored_packet.has_position == nil

      # The fix prevented the KeyError - that's what we're testing
      # Invalid coordinates are handled gracefully by the validation logic
    end

    test "handles ParseError struct in data_extended gracefully" do
      # This test covers the specific error case from the user's report
      # where data_extended contains a ParseError struct instead of MicE
      parse_error_data = %Aprs.Types.ParseError{
        error_code: :not_implemented,
        error_message: "PHG/DFS parsing not yet implemented",
        raw_data: nil
      }

      packet_data = %{
        base_callsign: "LU9DCE",
        ssid: "4",
        sender: "LU9DCE-4",
        destination: "ID",
        data_type: "phg_data",
        path: "qAR,N3HYM-9",
        information_field: "###################",
        data_extended: parse_error_data
      }

      # This should NOT raise an UndefinedFunctionError for ParseError.fetch/2
      # The fix ensures we check the struct type before using Access behavior
      assert {:ok, stored_packet} = Packets.store_packet(packet_data)

      # Verify the packet was stored correctly
      assert stored_packet.sender == "LU9DCE-4"
      assert stored_packet.data_type == "phg_data"

      # Should not have position data since ParseError doesn't contain coordinates
      assert stored_packet.has_position != true || stored_packet.has_position == nil
      assert is_nil(stored_packet.lat) || stored_packet.lat == nil
      assert is_nil(stored_packet.lon) || stored_packet.lon == nil
    end
  end
end
