defmodule Towerops.Snmp.Profiles.Vendors.ApcTest do
  use Towerops.DataCase, async: true

  import Mox

  alias Towerops.Snmp.Profiles.Vendors.Apc
  alias Towerops.Snmp.SnmpMock

  setup :verify_on_exit!

  @client_opts [
    ip: "192.168.1.1",
    community: "public",
    version: "2c",
    port: 161,
    timeout: 5000
  ]

  describe "profile_names/0" do
    test "returns APC profile names" do
      assert Apc.profile_names() == ["aos-emu2", "apc", "apc-cpdu", "apc-epdu", "apc-mgeups", "apc-netbotz"]
    end
  end

  describe "detect_hardware/1" do
    test "returns model when SNMP responds" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.318.1.1.1.1.1.1.0", _ ->
        {:ok, "Smart-UPS 3000"}
      end)

      assert Apc.detect_hardware(@client_opts) == "Smart-UPS 3000"
    end

    test "returns nil when SNMP fails" do
      expect(SnmpMock, :get, fn _, _, _ ->
        {:error, :timeout}
      end)

      assert Apc.detect_hardware(@client_opts) == nil
    end
  end

  describe "wireless_oid_defs/0" do
    test "returns list of sensor definitions" do
      defs = Apc.wireless_oid_defs()

      assert is_list(defs)
      assert [_ | _] = defs
    end

    test "includes battery capacity sensor" do
      defs = Apc.wireless_oid_defs()
      battery = Enum.find(defs, &(&1.sensor_descr == "Battery Capacity"))

      assert battery
      assert battery.sensor_type == "load"
      assert battery.sensor_unit == "%"
    end

    test "includes output voltage sensor" do
      defs = Apc.wireless_oid_defs()
      voltage = Enum.find(defs, &(&1.sensor_descr == "Output Voltage"))

      assert voltage
      assert voltage.sensor_type == "voltage"
      assert voltage.sensor_unit == "V"
    end

    test "includes output load sensor" do
      defs = Apc.wireless_oid_defs()
      load = Enum.find(defs, &(&1.sensor_descr == "Output Load"))

      assert load
      assert load.sensor_type == "load"
      assert load.sensor_unit == "%"
    end
  end

  describe "discover_wireless_sensors/1" do
    test "discovers sensors when SNMP responds" do
      expect(SnmpMock, :get, 9, fn _, oid, _ ->
        cond do
          String.contains?(oid, "318.1.1.1.2.2.1.0") -> {:ok, 95}
          String.contains?(oid, "318.1.1.1.2.2.2.0") -> {:ok, 25}
          String.contains?(oid, "318.1.1.1.2.2.3.0") -> {:ok, 36_000}
          String.contains?(oid, "318.1.1.1.4.2.1.0") -> {:ok, 120}
          String.contains?(oid, "318.1.1.1.4.2.2.0") -> {:ok, 60}
          String.contains?(oid, "318.1.1.1.4.2.3.0") -> {:ok, 45}
          String.contains?(oid, "318.1.1.1.4.2.4.0") -> {:ok, 5}
          String.contains?(oid, "318.1.1.1.3.2.1.0") -> {:ok, 121}
          String.contains?(oid, "318.1.1.1.3.2.4.0") -> {:ok, 60}
          true -> {:error, :no_such_object}
        end
      end)

      sensors = Apc.discover_wireless_sensors(@client_opts)

      assert is_list(sensors)
      assert length(sensors) == 9

      battery = Enum.find(sensors, &(&1.sensor_descr == "Battery Capacity"))
      assert battery.last_value == 95.0
    end

    test "returns empty list when no sensors respond" do
      expect(SnmpMock, :get, 9, fn _, _, _ ->
        {:error, :no_such_object}
      end)

      sensors = Apc.discover_wireless_sensors(@client_opts)

      assert sensors == []
    end
  end
end
