defmodule Towerops.Integration.SnmpIntegrationTest do
  @moduledoc """
  Integration tests for SNMP functionality against real devices.

  These tests are excluded from the main test suite by default.
  To run them: mix test --only integration

  Requirements:
  - Network access to test devices
  - SNMP-enabled devices (configure IP/community below)
  """
  use ExUnit.Case, async: false

  alias Towerops.Snmp.Client
  alias Towerops.Snmp.Poller
  alias Towerops.Workers.DeviceMonitorWorker

  @moduletag :real_device

  setup do
    # Skip these tests - they require real SNMP devices
    # To run: mix test --include real_device
    :ok
  end

  # Configure your test device here
  @test_device_opts [
    ip: "10.0.19.254",
    community: "kdyyJrT0Mm",
    version: "2c",
    port: 161,
    timeout: 5000
  ]

  describe "SNMP Client against real device" do
    test "can perform GET operation" do
      case Client.get(@test_device_opts, "1.3.6.1.2.1.1.3.0") do
        {:ok, uptime} ->
          assert is_integer(uptime)

        {:error, reason} ->
          flunk("SNMP GET failed: #{inspect(reason)}")
      end
    end

    test "can perform WALK operation" do
      case Client.walk(@test_device_opts, "1.3.6.1.2.1.2.2.1.1") do
        {:ok, results} ->
          assert is_map(results)
          assert map_size(results) > 0

        {:error, reason} ->
          flunk("SNMP WALK failed: #{inspect(reason)}")
      end
    end

    test "can test connection" do
      case Client.test_connection(@test_device_opts) do
        {:ok, _message} ->
          assert true

        {:error, reason} ->
          flunk("Connection test failed: #{inspect(reason)}")
      end
    end
  end

  describe "SNMP Poller against real device" do
    test "can check device reachability" do
      case Poller.check_device(@test_device_opts) do
        {:ok, response_time} ->
          assert is_float(response_time)
          assert response_time > 0

        {:error, reason} ->
          flunk("Poller check failed: #{inspect(reason)}")
      end
    end
  end

  describe "Discovery against real device" do
    @tag :full_discovery
    test "can discover device using Base profile" do
      alias Towerops.Snmp.Profiles.Base

      # Discover system info using Base profile
      {:ok, system_info} = Base.discover_system_info(@test_device_opts)
      assert is_binary(system_info.sys_descr)
      assert is_binary(system_info.sys_name)

      # Discover interfaces
      {:ok, interfaces} = Base.discover_interfaces(@test_device_opts)
      refute Enum.empty?(interfaces)

      # Discover sensors (may be empty if device doesn't support ENTITY-SENSOR-MIB)
      {:ok, sensors} = Base.discover_sensors(@test_device_opts)
      assert is_list(sensors)

      # Identify device
      device_info = Base.identify_device(system_info)
      assert is_binary(device_info.manufacturer)
      assert is_binary(device_info.model)
    end
  end

  describe "Device Monitor with SNMP" do
    test "creates SNMP-specific alert messages when device goes down" do
      # This test makes real SNMP calls to an unreachable IP to verify timeout handling
      import Towerops.AccountsFixtures

      user = user_fixture()
      {:ok, organization} = Towerops.Organizations.create_organization(%{name: "Test Org"}, user.id)

      {:ok, site} =
        Towerops.Sites.create_site(%{
          name: "Test Site",
          organization_id: organization.id
        })

      {:ok, device} =
        Towerops.Devices.create_device(%{
          name: "SNMP Router",
          ip_address: "192.0.2.1",
          site_id: site.id,
          organization_id: organization.id,
          snmp_enabled: true,
          snmp_version: "2c",
          snmp_community: "public",
          snmp_port: 161,
          monitoring_enabled: true
        })

      # Set to up first so status change will trigger
      {:ok, device} = Towerops.Devices.update_device_status(device, :up)

      # Trigger check - will timeout trying to reach 192.0.2.1
      DeviceMonitorWorker.perform(%Oban.Job{args: %{"device_id" => device.id}})

      alerts = Towerops.Alerts.list_devices_alerts(device.id)
      refute Enum.empty?(alerts)

      alert = hd(alerts)
      assert alert.alert_type == :device_down
      assert String.contains?(alert.message, "SNMP")
    end
  end
end
