defmodule Towerops.Snmp.Profiles.Vendors.Cambium do
  @moduledoc """
  SNMP profile for Cambium devices.

  Auto-generated from YAML profiles. Add vendor-specific OIDs as needed.
  """

  @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

  alias Towerops.Snmp.Client
  alias Towerops.Snmp.Profiles.Vendors.Vendor

  # Enterprise OID: 1.3.6.1.4.1.161

  @impl true
  def profile_names,
    do: ["cmm", "cmm4", "cnpilote", "cnpilotr", "epmp", "pmp", "ptp250", "ptp500", "ptp600", "ptp650", "ptp670", "ptp800"]

  @impl true
  def detect_hardware(client_opts) do
    case Client.get(client_opts, "1.3.6.1.2.1.1.1.0") do
      {:ok, descr} when is_binary(descr) ->
        parse_hardware_from_descr(descr)

      _ ->
        nil
    end
  end

  defp parse_hardware_from_descr(descr) do
    # For now, return first line or word from sysDescr
    descr
    |> String.split(~r/[\r\n]/)
    |> List.first()
    |> case do
      nil -> nil
      line -> String.slice(line, 0, 64)
    end
  end

  @impl true
  def wireless_oid_defs do
    [
      # Standard HOST-RESOURCES-MIB CPU
      %{
        oid: "1.3.6.1.2.1.25.3.3.1.2.1",
        sensor_descr: "CPU Load",
        sensor_type: "load",
        sensor_unit: "%",
        sensor_divisor: 1
      }
    ]
  end

  @impl true
  def discover_wireless_sensors(client_opts) do
    # Fetch static sensors
    static_sensors = Vendor.fetch_sensors(wireless_oid_defs(), client_opts)

    # Discover ePMP-specific wireless sensors
    epmp_sensors = discover_epmp_sensors(client_opts)

    static_sensors ++ epmp_sensors
  end

  # Discover ePMP-specific wireless sensors
  @spec discover_epmp_sensors(Client.connection_opts()) :: [map()]
  defp discover_epmp_sensors(client_opts) do
    # Check if this is an ePMP device by checking wirelessInterfaceMode
    case Client.get(client_opts, "1.3.6.1.4.1.17713.21.1.2.32.0") do
      {:ok, mode} when mode in [1, 2] ->
        build_epmp_sensors(client_opts, mode)

      _ ->
        []
    end
  end

  @spec build_epmp_sensors(Client.connection_opts(), integer()) :: [map()]
  defp build_epmp_sensors(client_opts, mode) do
    sensors = []

    # All ePMP devices get these sensors
    sensors =
      sensors ++
        [
          # RSSI - Downlink Receive Signal Strength Indicator
          %{
            oid: "1.3.6.1.4.1.17713.21.1.2.3.0",
            sensor_type: "rssi",
            sensor_descr: "Downlink RSSI",
            sensor_unit: "dBm",
            sensor_divisor: 1,
            sensor_index: "cambium_epmp_rssi"
          },
          # SNR - Downlink Signal-to-Noise Ratio
          %{
            oid: "1.3.6.1.4.1.17713.21.1.2.18.0",
            sensor_type: "snr",
            sensor_descr: "Downlink SNR",
            sensor_unit: "dB",
            sensor_divisor: 1,
            sensor_index: "cambium_epmp_snr"
          },
          # Frequency - RF frequency in MHz
          %{
            oid: "1.3.6.1.4.1.17713.21.1.2.1.0",
            sensor_type: "frequency",
            sensor_descr: "RF Frequency",
            sensor_unit: "MHz",
            sensor_divisor: 1,
            sensor_index: "cambium_epmp_frequency"
          }
        ]

    # Access Point mode (mode == 1) gets client count sensor
    sensors =
      if mode == 1 do
        sensors ++
          [
            %{
              oid: "1.3.6.1.4.1.17713.21.1.2.10.0",
              sensor_type: "clients",
              sensor_descr: "Connected Subscribers",
              sensor_unit: "",
              sensor_divisor: 1,
              sensor_index: "cambium_epmp_clients"
            }
          ]
      else
        sensors
      end

    # Fetch actual values for each sensor
    sensors
    |> Enum.map(fn sensor ->
      case Client.get(client_opts, sensor.oid) do
        {:ok, value} when is_integer(value) ->
          Map.put(sensor, :last_value, value)

        _ ->
          Map.put(sensor, :last_value, nil)
      end
    end)
    |> Enum.reject(&is_nil(&1.last_value))
  end
end
