defmodule Aprsme.DeviceParserTest do
  use ExUnit.Case, async: true
  use ExUnitProperties

  alias Aprsme.DeviceParser

  describe "extract_device_identifier/1" do
    test "returns the device_identifier atom-key value first" do
      assert DeviceParser.extract_device_identifier(%{device_identifier: "APRS123"}) == "APRS123"
    end

    test "returns the device_identifier string-key value" do
      assert DeviceParser.extract_device_identifier(%{"device_identifier" => "APRS123"}) ==
               "APRS123"
    end

    test "falls back to destination when device_identifier missing" do
      assert DeviceParser.extract_device_identifier(%{destination: "APN382"}) == "APN382"
      assert DeviceParser.extract_device_identifier(%{"destination" => "APN382"}) == "APN382"
    end

    test "prefers device_identifier over destination" do
      packet = %{device_identifier: "DIRECT", destination: "APN382"}
      assert DeviceParser.extract_device_identifier(packet) == "DIRECT"
    end

    test "falls back to symbol table/code from atom-keyed data_extended" do
      packet = %{data_extended: %{symbol_table_id: "/", symbol_code: ">"}}
      assert DeviceParser.extract_device_identifier(packet) == "/>"
    end

    test "falls back to symbol table/code from string-keyed data_extended" do
      packet = %{"data_extended" => %{"symbol_table_id" => "\\", "symbol_code" => "k"}}
      assert DeviceParser.extract_device_identifier(packet) == "\\k"
    end

    test "prefers destination over data_extended symbol (destination clause matches first)" do
      # Pattern-match order in find_device_identifier/1 checks destination before data_extended.
      packet = %{
        data_extended: %{symbol_table_id: "/", symbol_code: ">"},
        destination: "APN382"
      }

      assert DeviceParser.extract_device_identifier(packet) == "APN382"
    end

    test "falls back to data_extended symbol when no destination is set" do
      packet = %{data_extended: %{symbol_table_id: "/", symbol_code: ">"}}
      assert DeviceParser.extract_device_identifier(packet) == "/>"
    end

    test "returns nil for empty device_identifier strings" do
      assert DeviceParser.extract_device_identifier(%{device_identifier: ""}) == nil
    end

    test "returns nil for empty destination strings" do
      assert DeviceParser.extract_device_identifier(%{destination: ""}) == nil
    end

    test "returns nil for empty map" do
      assert DeviceParser.extract_device_identifier(%{}) == nil
    end

    test "returns nil for non-map input" do
      assert DeviceParser.extract_device_identifier(nil) == nil
      assert DeviceParser.extract_device_identifier("hello") == nil
      assert DeviceParser.extract_device_identifier(42) == nil
      assert DeviceParser.extract_device_identifier([]) == nil
    end

    test "returns nil when device_identifier is a non-binary truthy value" do
      assert DeviceParser.extract_device_identifier(%{device_identifier: :atom}) == nil
      assert DeviceParser.extract_device_identifier(%{device_identifier: 123}) == nil
    end
  end

  describe "normalize_device_identifier/1" do
    test "trims whitespace from strings" do
      assert DeviceParser.normalize_device_identifier("  APRS123  ") == "APRS123"
    end

    test "leaves clean strings alone" do
      assert DeviceParser.normalize_device_identifier("APRS123") == "APRS123"
    end

    test "converts atoms to strings" do
      assert DeviceParser.normalize_device_identifier(:yaesu_ft1d) == "yaesu_ft1d"
    end

    test "returns nil for nil" do
      assert DeviceParser.normalize_device_identifier(nil) == nil
    end

    test "returns nil for unsupported types" do
      assert DeviceParser.normalize_device_identifier(123) == nil
      assert DeviceParser.normalize_device_identifier(%{}) == nil
      assert DeviceParser.normalize_device_identifier([]) == nil
    end

    property "strings round-trip through normalization without surrounding whitespace" do
      check all(s <- string(:alphanumeric, min_length: 1)) do
        assert DeviceParser.normalize_device_identifier(s) == s
        assert DeviceParser.normalize_device_identifier("  " <> s <> "  ") == s
      end
    end

    property "atom inputs normalize to their string form" do
      check all(s <- string(:alphanumeric, min_length: 1, max_length: 10)) do
        atom = String.to_atom(s)
        assert DeviceParser.normalize_device_identifier(atom) == s
      end
    end
  end
end
