defmodule Towerops.Snmp.PollerTest do
  use Towerops.DataCase, async: true

  import Mox

  alias Towerops.Snmp.Poller
  alias Towerops.Snmp.SnmpMock

  setup :verify_on_exit!

  describe "check_device/1" do
    test "returns response time on successful check" do
      client_opts = [
        ip: "192.168.1.1",
        community: "public",
        version: "2c",
        port: 161,
        timeout: 5000
      ]

      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.3.0", _ ->
        {:ok, {:timeticks, 12_345_678}}
      end)

      assert {:ok, response_time} = Poller.check_device(client_opts)
      assert is_integer(response_time)
      assert response_time >= 0
    end

    test "returns error on timeout" do
      client_opts = [
        ip: "192.168.1.99",
        community: "public",
        version: "2c",
        port: 161,
        timeout: 1000
      ]

      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.3.0", _ ->
        {:error, :timeout}
      end)

      assert {:error, :timeout} = Poller.check_device(client_opts)
    end

    test "returns error on authentication failure" do
      client_opts = [
        ip: "192.168.1.1",
        community: "wrong_community",
        version: "2c",
        port: 161,
        timeout: 5000
      ]

      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.3.0", _ ->
        {:error, :auth_failure}
      end)

      assert {:error, :auth_failure} = Poller.check_device(client_opts)
    end

    test "returns error on network unreachable" do
      client_opts = [
        ip: "10.255.255.1",
        community: "public",
        version: "2c",
        port: 161,
        timeout: 5000
      ]

      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.3.0", _ ->
        {:error, :network_unreachable}
      end)

      assert {:error, :network_unreachable} = Poller.check_device(client_opts)
    end

    test "measures response time accurately" do
      client_opts = [
        ip: "192.168.1.1",
        community: "public",
        version: "2c",
        port: 161,
        timeout: 5000
      ]

      # Simulate a delayed response
      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.3.0", _ ->
        Process.sleep(10)
        {:ok, {:timeticks, 999}}
      end)

      assert {:ok, response_time} = Poller.check_device(client_opts)
      # Response time should be at least 10ms due to the sleep
      assert response_time >= 10
    end
  end

  describe "build_client_opts/1" do
    test "builds client options from device map" do
      device = %{
        ip_address: "192.168.1.100",
        snmp_community: "private",
        snmp_version: "2c",
        snmp_port: 161
      }

      client_opts = Poller.build_client_opts(device)

      assert client_opts[:ip] == "192.168.1.100"
      assert client_opts[:community] == "private"
      assert client_opts[:version] == "2c"
      assert client_opts[:port] == 161
      assert client_opts[:timeout] == 5000
    end

    test "uses default port 161 when snmp_port is nil" do
      device = %{
        ip_address: "192.168.1.100",
        snmp_community: "public",
        snmp_version: "2c",
        snmp_port: nil
      }

      client_opts = Poller.build_client_opts(device)

      assert client_opts[:port] == 161
    end

    test "handles custom SNMP port" do
      device = %{
        ip_address: "192.168.1.100",
        snmp_community: "public",
        snmp_version: "2c",
        snmp_port: 1161
      }

      client_opts = Poller.build_client_opts(device)

      assert client_opts[:port] == 1161
    end

    test "builds options for SNMPv1" do
      device = %{
        ip_address: "10.0.0.1",
        snmp_community: "public",
        snmp_version: "1",
        snmp_port: 161
      }

      client_opts = Poller.build_client_opts(device)

      assert client_opts[:version] == "1"
    end

    test "includes standard timeout of 5 seconds" do
      device = %{
        ip_address: "192.168.1.1",
        snmp_community: "public",
        snmp_version: "2c",
        snmp_port: 161
      }

      client_opts = Poller.build_client_opts(device)

      assert client_opts[:timeout] == 5000
    end
  end
end
