defmodule Towerops.Snmp.SanitizerTest do
  use ExUnit.Case, async: true

  alias Towerops.Snmp.Sanitizer

  describe "sanitize_string/1" do
    test "returns nil for nil input" do
      assert Sanitizer.sanitize_string(nil) == nil
    end

    test "returns trimmed string for printable input" do
      assert Sanitizer.sanitize_string("  Hello World  ") == "Hello World"
    end

    test "passes through normal strings" do
      assert Sanitizer.sanitize_string("test string") == "test string"
    end

    test "converts non-printable binary to hex string" do
      # Raw binary bytes (e.g., MAC address)
      assert Sanitizer.sanitize_string(<<255, 0, 128, 64>>) == "ff:00:80:40"
    end

    test "converts erlang charlist to string" do
      assert Sanitizer.sanitize_string(~c"hello") == "hello"
    end

    test "converts integers to string" do
      assert Sanitizer.sanitize_string(123) == "123"
    end

    test "handles mixed printable/non-printable as hex" do
      # String with null byte - not printable
      assert Sanitizer.sanitize_string(<<"test", 0>>) == "74:65:73:74:00"
    end
  end

  describe "sanitize_ipv4/1" do
    test "formats tuple as dotted decimal" do
      assert Sanitizer.sanitize_ipv4({192, 168, 1, 1}) == "192.168.1.1"
    end

    test "formats 4-byte binary as dotted decimal" do
      assert Sanitizer.sanitize_ipv4(<<192, 168, 1, 1>>) == "192.168.1.1"
    end

    test "passes through already-formatted string" do
      assert Sanitizer.sanitize_ipv4("10.0.0.1") == "10.0.0.1"
    end

    test "extracts first 4 bytes from longer binary" do
      assert Sanitizer.sanitize_ipv4(<<192, 168, 1, 1, 0, 0>>) == "192.168.1.1"
    end

    test "returns nil for invalid input" do
      assert Sanitizer.sanitize_ipv4("not an ip") == nil
      assert Sanitizer.sanitize_ipv4(nil) == nil
      assert Sanitizer.sanitize_ipv4(123) == nil
    end

    test "handles subnet mask with 0xff bytes" do
      # This was the original bug - 0xff is not valid UTF-8
      assert Sanitizer.sanitize_ipv4(<<255, 255, 255, 0>>) == "255.255.255.0"
    end
  end

  describe "sanitize_ipv6/1" do
    test "formats 16-byte binary as IPv6" do
      # ::1 (localhost)
      bytes = <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>
      assert Sanitizer.sanitize_ipv6(bytes) == "0:0:0:0:0:0:0:1"
    end

    test "passes through already-formatted string" do
      assert Sanitizer.sanitize_ipv6("2001:db8::1") == "2001:db8::1"
    end

    test "returns nil for invalid input" do
      assert Sanitizer.sanitize_ipv6("not an ipv6") == nil
      assert Sanitizer.sanitize_ipv6(nil) == nil
    end
  end

  describe "sanitize_mac/1" do
    test "formats 6-byte binary as colon-separated hex" do
      assert Sanitizer.sanitize_mac(<<0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF>>) == "aa:bb:cc:dd:ee:ff"
    end

    test "normalizes dash-separated to colon-separated" do
      assert Sanitizer.sanitize_mac("AA-BB-CC-DD-EE-FF") == "aa:bb:cc:dd:ee:ff"
    end

    test "passes through already-formatted lowercase" do
      assert Sanitizer.sanitize_mac("aa:bb:cc:dd:ee:ff") == "aa:bb:cc:dd:ee:ff"
    end

    test "lowercases uppercase input" do
      assert Sanitizer.sanitize_mac("AA:BB:CC:DD:EE:FF") == "aa:bb:cc:dd:ee:ff"
    end

    test "returns nil for invalid input" do
      assert Sanitizer.sanitize_mac("not a mac") == nil
      assert Sanitizer.sanitize_mac(nil) == nil
      assert Sanitizer.sanitize_mac(<<1, 2, 3>>) == nil
    end
  end

  describe "sanitize_map/1" do
    test "sanitizes all string values in map" do
      input = %{
        name: "  Test Name  ",
        value: 123,
        binary: <<255, 0>>
      }

      result = Sanitizer.sanitize_map(input)

      assert result.name == "Test Name"
      assert result.value == 123
      assert result.binary == "ff:00"
    end

    test "recursively sanitizes nested maps" do
      input = %{
        outer: "  outer  ",
        nested: %{
          inner: "  inner  "
        }
      }

      result = Sanitizer.sanitize_map(input)

      assert result.outer == "outer"
      assert result.nested.inner == "inner"
    end
  end
end
