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

  import Mox

  alias Towerops.Snmp.Profiles.Vendors.Pmp
  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 pmp profile name" do
      assert Pmp.profile_names() == ["pmp"]
    end
  end

  describe "detect_hardware/1" do
    test "detects PTP 450 model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "BHUL450 Connectorized"}}
      end)

      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.2.0", _ ->
        {:ok, {:octet_string, "generate timing"}}
      end)

      assert Pmp.detect_hardware(@client_opts) == "PTP 450 generate"
    end

    test "detects PTP 230 model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "BHUL Integrated"}}
      end)

      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.2.0", _ ->
        {:ok, {:octet_string, "receive timing"}}
      end)

      assert Pmp.detect_hardware(@client_opts) == "PTP 230 receive"
    end

    test "detects PMP 450 AP model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "MIMO OFDM AP"}}
      end)

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

      assert Pmp.detect_hardware(@client_opts) == "PMP 450 AP"
    end

    test "detects PMP 450 SM model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "MIMO OFDM SM"}}
      end)

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

      assert Pmp.detect_hardware(@client_opts) == "PMP 450 SM"
    end

    test "detects PMP 430 model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "OFDM AP"}}
      end)

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

      assert Pmp.detect_hardware(@client_opts) == "PMP 430 AP"
    end

    test "detects PMP 100 model" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:ok, {:octet_string, "FSK SM"}}
      end)

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

      assert Pmp.detect_hardware(@client_opts) == "PMP 100 SM"
    end

    test "returns nil when box type unavailable" do
      expect(SnmpMock, :get, fn _, "1.3.6.1.4.1.161.19.3.3.1.1.0", _ ->
        {:error, :no_such_object}
      end)

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

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

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

      # Verify structure of first definition
      first = hd(defs)
      assert Map.has_key?(first, :oid)
      assert Map.has_key?(first, :sensor_type)
      assert Map.has_key?(first, :sensor_descr)
      assert Map.has_key?(first, :sensor_unit)
      assert Map.has_key?(first, :sensor_divisor)
    end

    test "includes RSSI sensor" do
      defs = Pmp.wireless_oid_defs()
      rssi = Enum.find(defs, &(&1.sensor_type == "rssi"))

      assert rssi
      assert rssi.sensor_unit == "dBm"
    end

    test "includes SNR sensors" do
      defs = Pmp.wireless_oid_defs()
      snr_sensors = Enum.filter(defs, &(&1.sensor_type == "snr"))

      assert length(snr_sensors) >= 2
    end

    test "includes frequency sensor" do
      defs = Pmp.wireless_oid_defs()
      freq = Enum.find(defs, &(&1.sensor_type == "frequency"))

      assert freq
      assert freq.sensor_unit == "MHz"
      assert freq.sensor_divisor == 1000
    end

    test "includes clients sensor" do
      defs = Pmp.wireless_oid_defs()
      clients = Enum.find(defs, &(&1.sensor_type == "clients"))

      assert clients
    end

    test "includes utilization sensors" do
      defs = Pmp.wireless_oid_defs()
      util_sensors = Enum.filter(defs, &(&1.sensor_type == "utilization"))

      assert length(util_sensors) >= 2
    end
  end

  describe "discover_wireless_sensors/1" do
    test "discovers sensors when SNMP responds" do
      # Mock all sensor OIDs
      expect(SnmpMock, :get, 13, fn _, oid, _ ->
        cond do
          String.contains?(oid, "161.19.3.2.2.2.0") -> {:ok, {:integer, -65}}
          String.contains?(oid, "161.19.3.2.2.106.0") -> {:ok, {:integer, 25}}
          String.contains?(oid, "161.19.3.2.2.95.0") -> {:ok, {:integer, 23}}
          String.contains?(oid, "161.19.3.1.7.37.0") -> {:ok, {:integer, 5_800_000}}
          String.contains?(oid, "161.19.3.1.7.1.0") -> {:ok, {:integer, 15}}
          String.contains?(oid, "161.19.3.1.12.1.1.0") -> {:ok, {:integer, 45}}
          String.contains?(oid, "161.19.3.1.12.1.2.0") -> {:ok, {:integer, 30}}
          String.contains?(oid, "161.19.3.2.2.108.0") -> {:ok, {:integer, 10}}
          String.contains?(oid, "161.19.3.3.1.223.0") -> {:ok, {:integer, 5}}
          String.contains?(oid, "161.19.3.3.1.95.0") -> {:ok, {:integer, 2}}
          String.contains?(oid, "161.19.3.3.1.97.0") -> {:ok, {:integer, 1}}
          String.contains?(oid, "161.19.3.4.4.7.0") -> {:ok, {:integer, 12}}
          String.contains?(oid, "161.19.3.4.4.8.0") -> {:ok, {:integer, 8}}
          true -> {:error, :no_such_object}
        end
      end)

      sensors = Pmp.discover_wireless_sensors(@client_opts)

      assert is_list(sensors)
      assert length(sensors) == 13

      # Check RSSI sensor
      rssi = Enum.find(sensors, &(&1.sensor_descr == "RSSI"))
      assert rssi
      assert rssi.last_value == -65.0
    end

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

      sensors = Pmp.discover_wireless_sensors(@client_opts)

      assert sensors == []
    end
  end
end
