defmodule SnmpKit.SnmpLib.ManagerTest do
  use ExUnit.Case, async: true

  alias SnmpKit.SnmpLib.Error
  alias SnmpKit.SnmpLib.Manager
  alias SnmpKit.SnmpLib.Types

  doctest Manager

  @moduletag :manager_test

  describe "Manager.get/3" do
    test "performs basic GET operation with default options" do
      # Mock a simple GET response - this would normally connect to a real device
      # For testing, we can use a known OID that should work

      # Test OID normalization
      assert is_function(&Manager.get/3)

      # Test with list OID
      oid_list = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      assert {:error, _} = Manager.get("invalid.host.test", oid_list, timeout: 100)

      # Test with string OID
      oid_string = "1.3.6.1.2.1.1.1.0"
      assert {:error, _} = Manager.get("invalid.host.test", oid_string, timeout: 100)
    end

    test "validates input parameters" do
      # Test invalid host
      assert {:error, _} = Manager.get("", [1, 3, 6, 1], timeout: 50)

      # Test invalid OID (empty)
      assert {:error, _} = Manager.get("192.168.1.1", [], timeout: 50)

      # Test with valid parameters but non-existent host
      assert {:error, _} =
               Manager.get("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], timeout: 50)
    end

    test "handles community string options" do
      opts = [community: "private", timeout: 100]

      # Should attempt connection with private community
      assert {:error, _} = Manager.get("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], opts)
    end

    test "handles timeout options" do
      # Short timeout should fail quickly
      start_time = System.monotonic_time(:millisecond)
      {:error, _} = Manager.get("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], timeout: 50)
      end_time = System.monotonic_time(:millisecond)

      # Should complete within reasonable time of timeout
      # Allow for overhead: socket creation, encoding, etc. can add ~25ms
      # Using 200ms threshold to avoid flaky tests under load
      assert end_time - start_time < 200
    end

    test "normalizes OID formats correctly" do
      # Both should work the same way
      list_oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      string_oid = "1.3.6.1.2.1.1.1.0"

      result1 = Manager.get("invalid.host.test", list_oid, timeout: 100)
      result2 = Manager.get("invalid.host.test", string_oid, timeout: 100)

      # Both should fail the same way (since host is invalid)
      assert {:error, _} = result1
      assert {:error, _} = result2
    end
  end

  describe "Manager.get_bulk/3" do
    test "validates GETBULK requires SNMPv2c" do
      # v1 should be rejected
      assert {:error, :getbulk_requires_v2c} =
               Manager.get_bulk("192.168.1.1", [1, 3, 6, 1, 2, 1, 2, 2], version: :v1)

      # v2c should be accepted (but fail due to invalid host)
      assert {:error, _} =
               Manager.get_bulk("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2],
                 version: :v2c,
                 timeout: 100
               )
    end

    test "handles bulk operation parameters" do
      opts = [
        max_repetitions: 50,
        non_repeaters: 0,
        timeout: 100
      ]

      assert {:error, _} = Manager.get_bulk("invalid.host.test", [1, 3, 6, 1, 2, 1, 2, 2], opts)
    end

    test "validates bulk parameters" do
      # Should work with valid bulk parameters
      opts = [max_repetitions: 10, non_repeaters: 0, timeout: 100]
      assert {:error, _} = Manager.get_bulk("invalid.host.test", [1, 3, 6, 1], opts)
    end
  end

  describe "Manager.set/4" do
    test "accepts different value types" do
      host = "invalid.host.test"
      oid = [1, 3, 6, 1, 2, 1, 1, 5, 0]
      opts = [timeout: 100]

      # String value
      assert {:error, _} = Manager.set(host, oid, {:string, "test"}, opts)

      # Integer value
      assert {:error, _} = Manager.set(host, oid, {:integer, 42}, opts)

      # Counter32 value
      assert {:error, _} = Manager.set(host, oid, {:counter32, 123}, opts)
    end

    test "validates SET parameters" do
      opts = [timeout: 100]

      # Invalid value format should be handled
      assert {:error, _} = Manager.set("invalid.host.test", [1, 3, 6, 1], {:string, "test"}, opts)
    end
  end

  describe "Manager.get_multi/3" do
    test "handles multiple OIDs efficiently" do
      oids = [
        [1, 3, 6, 1, 2, 1, 1, 1, 0],
        [1, 3, 6, 1, 2, 1, 1, 3, 0],
        [1, 3, 6, 1, 2, 1, 1, 5, 0]
      ]

      opts = [timeout: 100]

      # Should return results for all OIDs (errors in this case)
      assert {:error, _} = Manager.get_multi("invalid.host.test", oids, opts)
    end

    test "validates multi-get parameters" do
      # Empty OID list should be handled
      assert {:error, _} = Manager.get_multi("192.168.1.1", [], timeout: 100)

      # Invalid OIDs should be handled - empty list case
      assert {:error, _} = Manager.get_multi("invalid.host.test", [], timeout: 100)
    end
  end

  describe "Manager.ping/2" do
    test "performs SNMP reachability test" do
      # Should attempt sysUpTime GET
      assert {:error, _} = Manager.ping("invalid.host.test", timeout: 100)

      # Test with custom community
      assert {:error, _} = Manager.ping("invalid.host.test", community: "private", timeout: 100)
    end

    test "validates ping parameters" do
      # Should handle various input formats
      assert {:error, _} = Manager.ping("", timeout: 100)
      assert {:error, _} = Manager.ping("192.168.255.255", timeout: 50)
    end
  end

  describe "Manager option handling" do
    test "merges default options correctly" do
      # Test that defaults are applied
      assert {:error, _} = Manager.get("invalid.host.test", [1, 3, 6, 1])

      # Test that custom options override defaults
      custom_opts = [
        community: "test",
        version: :v1,
        timeout: 200,
        port: 1161
      ]

      assert {:error, _} = Manager.get("invalid.host.test", [1, 3, 6, 1], custom_opts)
    end

    test "validates option values" do
      # Test various option combinations
      opts = [
        community: "public",
        version: :v2c,
        timeout: 5000,
        retries: 3,
        port: 161,
        local_port: 0
      ]

      assert {:error, _} = Manager.get("invalid.host.test", [1, 3, 6, 1], opts)
    end
  end

  describe "Manager host:port parsing" do
    test "supports host:port string format (backward compatibility)" do
      # Host with port in string format should work
      assert {:error, _} = Manager.get("invalid.host.test:1161", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get("192.168.255.255:162", [1, 3, 6, 1], timeout: 100)

      # Test with get_bulk
      assert {:error, _} = Manager.get_bulk("invalid.host.test:1161", [1, 3, 6, 1], timeout: 100)

      # Test with set
      assert {:error, _} =
               Manager.set("invalid.host.test:1161", [1, 3, 6, 1], {:string, "test"}, timeout: 100)

      # Test with get_multi
      oids = [[1, 3, 6, 1, 2, 1, 1, 1, 0], [1, 3, 6, 1, 2, 1, 1, 3, 0]]
      assert {:error, _} = Manager.get_multi("invalid.host.test:1161", oids, timeout: 100)

      # Test with ping
      assert {:error, _} = Manager.ping("invalid.host.test:1161", timeout: 100)
    end

    test "supports :port option format (new functionality)" do
      # Host without port, using :port option
      assert {:error, _} =
               Manager.get("invalid.host.test", [1, 3, 6, 1], port: 1161, timeout: 100)

      assert {:error, _} = Manager.get("192.168.255.255", [1, 3, 6, 1], port: 162, timeout: 100)

      # Test with get_bulk
      assert {:error, _} =
               Manager.get_bulk("invalid.host.test", [1, 3, 6, 1], port: 1161, timeout: 100)

      # Test with set
      assert {:error, _} =
               Manager.set("invalid.host.test", [1, 3, 6, 1], {:string, "test"},
                 port: 1161,
                 timeout: 100
               )

      # Test with get_multi
      oids = [[1, 3, 6, 1, 2, 1, 1, 1, 0], [1, 3, 6, 1, 2, 1, 1, 3, 0]]
      assert {:error, _} = Manager.get_multi("invalid.host.test", oids, port: 1161, timeout: 100)

      # Test with ping
      assert {:error, _} = Manager.ping("invalid.host.test", port: 1161, timeout: 100)
    end

    test "host:port string takes precedence over :port option" do
      # When both are specified, host:port should take precedence for backward compatibility
      # We can't easily test the actual port being used without mocking transport,
      # but we can verify both forms don't cause errors
      assert {:error, _} =
               Manager.get("invalid.host.test:1161", [1, 3, 6, 1], port: 162, timeout: 100)

      assert {:error, _} =
               Manager.get_bulk("invalid.host.test:1161", [1, 3, 6, 1], port: 162, timeout: 100)

      assert {:error, _} =
               Manager.set("invalid.host.test:1161", [1, 3, 6, 1], {:string, "test"},
                 port: 162,
                 timeout: 100
               )

      # Test with get_multi
      oids = [[1, 3, 6, 1, 2, 1, 1, 1, 0], [1, 3, 6, 1, 2, 1, 1, 3, 0]]

      assert {:error, _} =
               Manager.get_multi("invalid.host.test:1161", oids, port: 162, timeout: 100)

      # Test with ping
      assert {:error, _} = Manager.ping("invalid.host.test:1161", port: 162, timeout: 100)
    end

    test "handles IPv6 addresses without confusing them with port specifications" do
      # Plain IPv6 addresses contain colons but shouldn't be treated as host:port
      # Use invalid IPv6 addresses to avoid network calls
      assert {:error, _} = Manager.get("invalid::ipv6", [1, 3, 6, 1], timeout: 25)
      assert {:error, _} = Manager.get("test:db8::invalid", [1, 3, 6, 1], timeout: 25)
      assert {:error, _} = Manager.get("fake::1234:5678:9abc:def0", [1, 3, 6, 1], timeout: 25)

      # IPv6 with :port option should work (but fail due to invalid host)
      assert {:error, _} = Manager.get("invalid::ipv6", [1, 3, 6, 1], port: 1161, timeout: 25)

      assert {:error, _} =
               Manager.get("test:db8::invalid", [1, 3, 6, 1], port: 1161, timeout: 25)

      assert {:error, _} =
               Manager.get("fake::1234:5678:9abc:def0", [1, 3, 6, 1], port: 1161, timeout: 25)
    end

    test "supports RFC 3986 bracket notation for IPv6 with ports" do
      # IPv6 with port using bracket notation [addr]:port
      assert {:error, _} = Manager.get("[::1]:1161", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get("[2001:db8::1]:162", [1, 3, 6, 1], timeout: 100)

      assert {:error, _} =
               Manager.get("[fe80::1234:5678:9abc:def0]:2001", [1, 3, 6, 1], timeout: 100)

      # Test with get_bulk
      assert {:error, _} = Manager.get_bulk("[::1]:1161", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get_bulk("[2001:db8::1]:162", [1, 3, 6, 1], timeout: 100)

      # Test with set
      assert {:error, _} =
               Manager.set("[::1]:1161", [1, 3, 6, 1], {:string, "test"}, timeout: 100)

      assert {:error, _} =
               Manager.set("[2001:db8::1]:162", [1, 3, 6, 1], {:string, "test"}, timeout: 100)

      # Test with get_multi
      oids = [[1, 3, 6, 1, 2, 1, 1, 1, 0], [1, 3, 6, 1, 2, 1, 1, 3, 0]]
      assert {:error, _} = Manager.get_multi("[::1]:1161", oids, timeout: 100)
      assert {:error, _} = Manager.get_multi("[2001:db8::1]:162", oids, timeout: 100)

      # Test with ping
      assert {:error, _} = Manager.ping("[::1]:1161", timeout: 100)
      assert {:error, _} = Manager.ping("[2001:db8::1]:162", timeout: 100)
    end

    test "bracket notation takes precedence over :port option for IPv6" do
      # When both bracket notation and :port option are provided, bracket should take precedence
      assert {:error, _} = Manager.get("[::1]:1161", [1, 3, 6, 1], port: 162, timeout: 100)

      assert {:error, _} =
               Manager.get("[2001:db8::1]:2001", [1, 3, 6, 1], port: 161, timeout: 100)
    end

    test "handles malformed IPv6 bracket notation gracefully" do
      # Invalid bracket notation should be treated as hostnames and not cause crashes
      # Missing closing bracket
      assert {:error, _} = Manager.get("[::1", [1, 3, 6, 1], timeout: 25)
      # Missing opening bracket
      assert {:error, _} = Manager.get("::1]", [1, 3, 6, 1], timeout: 25)
      # Invalid format
      assert {:error, _} = Manager.get("[::1:abc", [1, 3, 6, 1], timeout: 25)
      # Invalid port
      assert {:error, _} = Manager.get("[::1]:99999", [1, 3, 6, 1], timeout: 25)
      # Non-numeric port
      assert {:error, _} = Manager.get("[::1]:abc", [1, 3, 6, 1], timeout: 25)
    end

    test "handles mixed IPv4/IPv6 scenarios correctly" do
      # IPv4 with port should still work
      assert {:error, _} = Manager.get("192.168.1.1:1161", [1, 3, 6, 1], timeout: 100)

      # IPv6 without port should use :port option
      assert {:error, _} = Manager.get("::1", [1, 3, 6, 1], port: 1161, timeout: 100)

      # IPv6 with bracket notation should override :port option
      assert {:error, _} = Manager.get("[::1]:2001", [1, 3, 6, 1], port: 1161, timeout: 100)

      # Complex IPv6 addresses should work with both patterns
      assert {:error, _} =
               Manager.get("2001:0db8:85a3:0000:0000:8a2e:0370:7334", [1, 3, 6, 1],
                 port: 1161,
                 timeout: 100
               )

      assert {:error, _} =
               Manager.get("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:2002", [1, 3, 6, 1], timeout: 100)
    end

    test "validates port numbers in host:port format" do
      # Invalid port numbers should be handled gracefully
      assert {:error, _} = Manager.get("invalid.host.test:99999", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get("invalid.host.test:0", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get("invalid.host.test:abc", [1, 3, 6, 1], timeout: 100)
    end

    test "falls back to default port 161 when no port specified" do
      # No port in host string and no :port option should use default 161
      assert {:error, _} = Manager.get("invalid.host.test", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get("192.168.255.255", [1, 3, 6, 1], timeout: 100)
    end
  end

  describe "Manager error handling" do
    test "handles network errors gracefully" do
      # Network errors (timeout or network unreachable)
      assert {:error, _} = Manager.get("192.168.255.255", [1, 3, 6, 1], timeout: 50)

      # Network unreachable errors (using non-routable IP to avoid DNS lookup)
      assert {:error, _} = Manager.get("192.0.2.1", [1, 3, 6, 1], timeout: 50)
    end

    test "handles SNMP protocol errors" do
      # These would test actual SNMP error responses
      # For now, we test that error handling structure is in place
      assert is_function(&Manager.get/3)
      assert is_function(&Manager.get_bulk/3)
      assert is_function(&Manager.set/4)
    end
  end

  describe "Manager error status mapping" do
    test "correctly maps SNMP error status codes" do
      # Test that Manager.decode_error_status works correctly through public API
      # We'll test this by simulating PDU responses with different error status codes

      # This tests the internal logic but through a way that's observable
      # The decode_error_status function should map:
      # 0 -> :no_error, 1 -> :too_big, 2 -> :no_such_name, 3 -> :bad_value, 4 -> :read_only, 5 -> :gen_err

      # We can verify this works by using the Error module which should have the same mapping
      assert Error.error_atom(0) == :no_error
      assert Error.error_atom(1) == :too_big
      assert Error.error_atom(2) == :no_such_name
      assert Error.error_atom(3) == :bad_value
      assert Error.error_atom(4) == :read_only
      assert Error.error_atom(5) == :gen_err
    end

    test "error status takes precedence over varbinds" do
      # This test verifies that when error_status != 0, it's handled immediately
      # regardless of what's in varbinds (which is the bug we just fixed)

      # The fix ensures that error_status is checked BEFORE varbinds pattern matching
      # This is critical for proper SNMP error handling

      # We can't directly test the private extract_get_result function,
      # but we can verify the logic is sound by testing error interpretation
      # The interpret_error function only changes :gen_err, other errors pass through
      assert Manager.interpret_error(:no_such_name, :get, :v2c) == :no_such_name
      assert Manager.interpret_error(:gen_err, :get, :v1) == :no_such_name
    end

    test "handles edge cases in error responses" do
      # Verify error handling doesn't break with various inputs
      assert Manager.interpret_error(:timeout, :get, :v2c) == :timeout
      assert Manager.interpret_error(:network_error, :get, :v1) == :network_error
      assert Manager.interpret_error(:invalid_response, :get, :v2c) == :invalid_response
    end

    test "correctly handles SNMPv2c exception values in varbinds" do
      # Test that exception values are properly extracted regardless of format
      # The fix handles both formats:
      # 1. {oid, :end_of_mib_view, nil} - simulator format (type field)
      # 2. {oid, :octet_string, {:end_of_mib_view, nil}} - standard format (value field)

      # This verifies the logic works but we can't test the private function directly
      # Instead verify that SNMPv2c exception types are recognized
      assert Types.exception_type?(:no_such_object) == true
      assert Types.exception_type?(:no_such_instance) == true
      assert Types.exception_type?(:end_of_mib_view) == true
      assert Types.exception_type?(:integer) == false
    end
  end

  describe "Manager error interpretation" do
    test "interpret_error provides better semantics for genErr" do
      # SNMPv1 GET operations
      assert Manager.interpret_error(:gen_err, :get, :v1) == :no_such_name

      # SNMPv2c+ GET operations
      assert Manager.interpret_error(:gen_err, :get, :v2c) == :no_such_object
      assert Manager.interpret_error(:gen_err, :get, :v2) == :no_such_object
      assert Manager.interpret_error(:gen_err, :get, :v3) == :no_such_object

      # SNMPv2c+ GETBULK operations
      assert Manager.interpret_error(:gen_err, :get_bulk, :v2c) == :no_such_object
      assert Manager.interpret_error(:gen_err, :get_bulk, :v2) == :no_such_object

      # Other errors pass through unchanged
      assert Manager.interpret_error(:too_big, :get, :v2c) == :too_big
      assert Manager.interpret_error(:no_such_name, :get, :v1) == :no_such_name
      assert Manager.interpret_error(:bad_value, :set, :v2c) == :bad_value

      # SET operations with genErr remain as genErr
      assert Manager.interpret_error(:gen_err, :set, :v2c) == :gen_err
    end

    test "interpret_error handles edge cases" do
      # Unknown operation types
      assert Manager.interpret_error(:gen_err, :unknown_op, :v2c) == :gen_err

      # Unknown SNMP versions
      assert Manager.interpret_error(:gen_err, :get, :unknown_version) == :gen_err

      # nil or invalid inputs
      assert Manager.interpret_error(nil, :get, :v2c) == nil
      assert Manager.interpret_error(:gen_err, nil, :v2c) == :gen_err
    end
  end

  describe "Manager integration" do
    test "integrates with existing SnmpLib modules" do
      # Verify Manager uses other SnmpLib modules correctly

      # Test OID normalization (should use SnmpKit.SnmpLib.OID)
      string_oid = "1.3.6.1.2.1.1.1.0"
      assert {:error, _} = Manager.get("invalid.host.test", string_oid, timeout: 100)

      # Test PDU creation (should use SnmpKit.SnmpLib.PDU)
      assert {:error, _} = Manager.get("invalid.host.test", [1, 3, 6, 1], timeout: 100)

      # Test transport (should use SnmpKit.SnmpLib.Transport)
      assert {:error, _} = Manager.ping("invalid.host.test", timeout: 100)
    end
  end

  # Performance and stress tests
  describe "Manager performance" do
    @tag :performance
    test "handles concurrent operations" do
      # Test multiple concurrent operations
      tasks =
        Enum.map(1..5, fn _i ->
          Task.async(fn ->
            Manager.get("invalid.host.test", [1, 3, 6, 1], timeout: 100)
          end)
        end)

      results = Task.await_many(tasks, 1000)

      # All should fail gracefully (invalid host)
      assert Enum.all?(results, fn result ->
               match?({:error, _}, result)
             end)
    end

    @tag :performance
    test "get_multi is more efficient than individual gets" do
      oids = Enum.map(1..10, fn i -> [1, 3, 6, 1, 2, 1, 1, i, 0] end)

      # Time individual gets
      {time_individual, _} =
        :timer.tc(fn ->
          Enum.map(oids, fn oid ->
            Manager.get("invalid.host.test", oid, timeout: 50)
          end)
        end)

      # Time multi get
      {time_multi, _} =
        :timer.tc(fn ->
          Manager.get_multi("invalid.host.test", oids, timeout: 50)
        end)

      # Multi should complete faster (though both will fail)
      # At minimum, they should both complete within reasonable time
      assert time_individual > 0
      assert time_multi > 0
    end
  end

  describe "Manager.get_next/3" do
    test "performs basic GETNEXT operation with default options" do
      # Test function exists and has correct signature
      assert is_function(&Manager.get_next/3)

      # Test with list OID - should fail with invalid host but function should work
      oid_list = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      assert {:error, _} = Manager.get_next("invalid.host.test", oid_list, timeout: 100)

      # Test with string OID
      oid_string = "1.3.6.1.2.1.1.1.0"
      assert {:error, _} = Manager.get_next("invalid.host.test", oid_string, timeout: 100)
    end

    test "uses proper GETNEXT PDU for SNMP v1" do
      # SNMP v1 should use actual GETNEXT PDU, not GETBULK
      oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      opts = [version: :v1, timeout: 100]

      # Should attempt GETNEXT operation (will fail due to invalid host)
      assert {:error, _} = Manager.get_next("invalid.host.test", oid, opts)
    end

    test "uses GETBULK with max_repetitions=1 for SNMP v2c+" do
      # SNMP v2c should use GETBULK for efficiency
      oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      opts = [version: :v2c, timeout: 100]

      # Should attempt GETBULK operation (will fail due to invalid host)
      assert {:error, _} = Manager.get_next("invalid.host.test", oid, opts)
    end

    test "validates input parameters" do
      # Test invalid host
      assert {:error, _} = Manager.get_next("", [1, 3, 6, 1], timeout: 50)

      # Test invalid OID (empty)
      assert {:error, _} = Manager.get_next("192.168.1.1", [], timeout: 50)

      # Test with valid parameters but non-existent host
      assert {:error, _} =
               Manager.get_next("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], timeout: 50)
    end

    test "handles community string options" do
      opts = [community: "private", timeout: 100]

      # Should attempt connection with private community
      assert {:error, _} =
               Manager.get_next("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], opts)
    end

    test "handles timeout options" do
      # Short timeout should fail quickly
      start_time = System.monotonic_time(:millisecond)
      {:error, _} = Manager.get_next("192.0.2.1", [1, 3, 6, 1, 2, 1, 1, 1, 0], timeout: 50)
      end_time = System.monotonic_time(:millisecond)

      # Should complete within reasonable time of timeout
      # Allow for overhead: socket creation, encoding, etc. can add ~25ms
      # Using 200ms threshold to avoid flaky tests under load
      assert end_time - start_time < 200
    end

    test "normalizes OID formats correctly" do
      # Both should work the same way
      list_oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]
      string_oid = "1.3.6.1.2.1.1.1.0"

      result1 = Manager.get_next("invalid.host.test", list_oid, timeout: 100)
      result2 = Manager.get_next("invalid.host.test", string_oid, timeout: 100)

      # Both should fail the same way (since host is invalid)
      assert {:error, _} = result1
      assert {:error, _} = result2
    end

    test "supports both SNMP versions" do
      oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]

      # Test v1 explicitly
      assert {:error, _} = Manager.get_next("invalid.host.test", oid, version: :v1, timeout: 100)

      # Test v2c explicitly
      assert {:error, _} = Manager.get_next("invalid.host.test", oid, version: :v2c, timeout: 100)

      # Test default (should be v2c)
      assert {:error, _} = Manager.get_next("invalid.host.test", oid, timeout: 100)
    end

    test "handles host:port format correctly" do
      # Host with port in string format should work
      assert {:error, _} = Manager.get_next("invalid.host.test:1161", [1, 3, 6, 1], timeout: 100)
      assert {:error, _} = Manager.get_next("192.168.255.255:162", [1, 3, 6, 1], timeout: 100)

      # IPv6 bracket notation
      assert {:error, _} = Manager.get_next("[::1]:1161", [1, 3, 6, 1], timeout: 100)

      # Port option
      assert {:error, _} =
               Manager.get_next("invalid.host.test", [1, 3, 6, 1], port: 1161, timeout: 100)
    end

    test "expected return format is {:ok, {next_oid, type, value}} tuple" do
      # While we can't test successful responses without a real SNMP device,
      # we can verify the function signature and error return format
      result = Manager.get_next("invalid.host.test", [1, 3, 6, 1, 2, 1, 1, 1, 0], timeout: 100)

      # Should return error tuple (since host is invalid)
      assert {:error, _reason} = result

      # The successful format would be {:ok, {next_oid, type, value}}
      # This is documented in the function spec and examples
    end

    test "handles various option combinations" do
      oid = [1, 3, 6, 1, 2, 1, 1, 1, 0]

      # Test with multiple options
      opts = [
        community: "test",
        version: :v1,
        timeout: 200,
        retries: 2,
        port: 1161,
        local_port: 0
      ]

      assert {:error, _} = Manager.get_next("invalid.host.test", oid, opts)

      # Test v2c with different options
      opts_v2c = [
        community: "public",
        version: :v2c,
        timeout: 500,
        retries: 1,
        port: 161
      ]

      assert {:error, _} = Manager.get_next("invalid.host.test", oid, opts_v2c)
    end
  end
end
