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

  import Mox

  alias Towerops.Snmp.Profiles.Vendors.Omnitron
  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 Omnitron profile names" do
      assert Omnitron.profile_names() == ["omnitron-iconverter"]
    end
  end

  describe "detect_hardware/1" do
    test "parses hardware from sysDescr with full format" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.1.0", _ ->
        {:ok, "Omnitron iConverter GX/TM2 v1.2.3 s/n 12345"}
      end)

      assert Omnitron.detect_hardware(@client_opts) == "iConverter GX/TM2"
    end

    test "handles sysDescr without model pattern" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.1.0", _ ->
        {:ok, "Some other format"}
      end)

      assert Omnitron.detect_hardware(@client_opts) == "Some other format"
    end

    test "truncates long descriptions to 64 characters" do
      long_descr = String.duplicate("A", 100)

      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.1.0", _ ->
        {:ok, long_descr}
      end)

      result = Omnitron.detect_hardware(@client_opts)
      assert String.length(result) == 64
    end

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

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

    test "handles multi-line descriptions" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.2.1.1.1.0", _ ->
        {:ok, "First line\r\nSecond line"}
      end)

      assert Omnitron.detect_hardware(@client_opts) == "First line"
    end
  end

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

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

    test "includes CPU temperature sensor" do
      defs = Omnitron.wireless_oid_defs()
      temp = Enum.find(defs, &(&1.sensor_descr == "CPU Ambient Temperature"))

      assert temp
      assert temp.sensor_type == "temperature"
      assert temp.sensor_unit == "°C"
      assert temp.sensor_divisor == 1
      assert temp.oid == "1.3.6.1.4.1.7342.2.1.3.1.38.1"
    end

    test "includes module temperature sensor" do
      defs = Omnitron.wireless_oid_defs()
      temp = Enum.find(defs, &(&1.sensor_descr == "Module Internal Ambient Temperature"))

      assert temp
      assert temp.sensor_type == "temperature"
      assert temp.sensor_unit == "°C"
      assert temp.sensor_divisor == 1
      assert temp.oid == "1.3.6.1.4.1.7342.2.1.11.1.9.1"
    end

    test "includes CPU input voltage sensor" do
      defs = Omnitron.wireless_oid_defs()
      voltage = Enum.find(defs, &(&1.sensor_descr == "CPU Input Voltage"))

      assert voltage
      assert voltage.sensor_type == "voltage"
      assert voltage.sensor_unit == "V"
      assert voltage.sensor_divisor == 1000
      assert voltage.oid == "1.3.6.1.4.1.7342.2.1.3.1.36.1"
    end

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

      assert voltage
      assert voltage.sensor_type == "voltage"
      assert voltage.sensor_unit == "V"
      assert voltage.sensor_divisor == 1000
      assert voltage.oid == "1.3.6.1.4.1.7342.2.1.3.1.37.1"
    end

    test "each definition has required fields" do
      defs = Omnitron.wireless_oid_defs()

      Enum.each(defs, fn def ->
        assert Map.has_key?(def, :oid)
        assert Map.has_key?(def, :sensor_descr)
        assert Map.has_key?(def, :sensor_type)
        assert Map.has_key?(def, :sensor_unit)
        assert Map.has_key?(def, :sensor_divisor)
      end)
    end
  end

  describe "discover_wireless_sensors/1" do
    test "discovers sensors when SNMP responds" do
      expect(SnmpMock, :get, 4, fn _, oid, _ ->
        cond do
          String.contains?(oid, "7342.2.1.3.1.38.1") -> {:ok, 35}
          String.contains?(oid, "7342.2.1.11.1.9.1") -> {:ok, 42}
          String.contains?(oid, "7342.2.1.3.1.36.1") -> {:ok, 12_000}
          String.contains?(oid, "7342.2.1.3.1.37.1") -> {:ok, 5_000}
          true -> {:error, :no_such_object}
        end
      end)

      sensors = Omnitron.discover_wireless_sensors(@client_opts)

      assert is_list(sensors)
      assert length(sensors) == 4

      cpu_temp = Enum.find(sensors, &(&1.sensor_descr == "CPU Ambient Temperature"))
      assert cpu_temp.last_value == 35.0

      module_temp = Enum.find(sensors, &(&1.sensor_descr == "Module Internal Ambient Temperature"))
      assert module_temp.last_value == 42.0

      input_voltage = Enum.find(sensors, &(&1.sensor_descr == "CPU Input Voltage"))
      assert input_voltage.last_value == 12_000.0
      assert input_voltage.sensor_divisor == 1000

      output_voltage = Enum.find(sensors, &(&1.sensor_descr == "CPU Output Voltage"))
      assert output_voltage.last_value == 5_000.0
      assert output_voltage.sensor_divisor == 1000
    end

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

      sensors = Omnitron.discover_wireless_sensors(@client_opts)

      assert sensors == []
    end

    test "filters out failed sensors but returns successful ones" do
      expect(SnmpMock, :get, 4, fn _, oid, _ ->
        cond do
          String.contains?(oid, "7342.2.1.3.1.38.1") -> {:ok, 35}
          String.contains?(oid, "7342.2.1.11.1.9.1") -> {:error, :no_such_object}
          String.contains?(oid, "7342.2.1.3.1.36.1") -> {:ok, 12_000}
          String.contains?(oid, "7342.2.1.3.1.37.1") -> {:error, :timeout}
          true -> {:error, :no_such_object}
        end
      end)

      sensors = Omnitron.discover_wireless_sensors(@client_opts)

      assert length(sensors) == 2
      assert Enum.any?(sensors, &(&1.sensor_descr == "CPU Ambient Temperature"))
      assert Enum.any?(sensors, &(&1.sensor_descr == "CPU Input Voltage"))
    end
  end
end
