defmodule Towerops.Snmp.Profiles.Vendors.Pmp do
  @moduledoc """
  Cambium PMP device-specific SNMP handling.

  Mirrors LibreNMS/OS/Pmp.php for wireless sensor discovery and hardware detection.

  Supported devices:
  - PMP 450 (MIMO OFDM)
  - PMP 430 (OFDM)
  - PMP 100 (FSK)
  - PTP 450, PTP 230, PTP 100 (Backhaul)
  """

  @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

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

  # OIDs for hardware detection
  @box_device_type "1.3.6.1.4.1.161.19.3.3.1.1.0"
  @bh_timing_mode "1.3.6.1.4.1.161.19.3.3.1.2.0"

  @impl true
  def profile_names, do: ["pmp"]

  @impl true
  def detect_hardware(client_opts) do
    case Client.get(client_opts, @box_device_type) do
      {:ok, box_type} when is_binary(box_type) ->
        timing_mode =
          case Client.get(client_opts, @bh_timing_mode) do
            {:ok, mode} when is_binary(mode) -> mode
            _ -> ""
          end

        determine_hardware(box_type, timing_mode)

      _ ->
        nil
    end
  end

  defp determine_hardware(box_type, timing_mode) do
    ptp_model = detect_ptp_model(box_type, timing_mode)
    ptp_model || detect_pmp_model(box_type)
  end

  defp detect_ptp_model(box_type, timing_mode) do
    ptp_models = [
      {"BHUL450", "PTP 450"},
      {"BHUL", "PTP 230"},
      {"BH20", "PTP 100"}
    ]

    Enum.find_value(ptp_models, fn {pattern, model} ->
      if String.contains?(box_type, pattern) do
        "#{model} #{clean_timing_mode(timing_mode)}"
      end
    end)
  end

  defp detect_pmp_model(box_type) do
    {base_model, suffix} =
      cond do
        String.contains?(box_type, "MIMO OFDM") -> {"PMP 450", ap_or_sm(box_type)}
        String.contains?(box_type, "OFDM") -> {"PMP 430", ap_or_sm(box_type)}
        true -> {"PMP 100", ap_or_sm(box_type)}
      end

    "#{base_model} #{suffix}"
  end

  defp ap_or_sm(box_type) do
    if String.contains?(box_type, "AP"), do: "AP", else: "SM"
  end

  defp clean_timing_mode(mode) do
    mode
    |> String.replace("timing", "")
    |> String.replace("timeing", "")
    |> String.trim()
  end

  @impl true
  def discover_wireless_sensors(client_opts) do
    Vendor.fetch_sensors(wireless_oid_defs(), client_opts)
  end

  @impl true
  def wireless_oid_defs do
    [
      # RSSI
      %{
        oid: "1.3.6.1.4.1.161.19.3.2.2.2.0",
        sensor_type: "rssi",
        sensor_descr: "RSSI",
        sensor_unit: "dBm",
        sensor_divisor: 1
      },
      # SNR Horizontal (SM)
      %{
        oid: "1.3.6.1.4.1.161.19.3.2.2.106.0",
        sensor_type: "snr",
        sensor_descr: "SNR Horizontal",
        sensor_unit: "dB",
        sensor_divisor: 1
      },
      # SNR Vertical (SM)
      %{
        oid: "1.3.6.1.4.1.161.19.3.2.2.95.0",
        sensor_type: "snr",
        sensor_descr: "SNR Vertical",
        sensor_unit: "dB",
        sensor_divisor: 1
      },
      # Frequency (in 10s of KHz for OFDM, divide by 1000)
      %{
        oid: "1.3.6.1.4.1.161.19.3.1.7.37.0",
        sensor_type: "frequency",
        sensor_descr: "Frequency",
        sensor_unit: "MHz",
        sensor_divisor: 1000
      },
      # Client count (AP only)
      %{
        oid: "1.3.6.1.4.1.161.19.3.1.7.1.0",
        sensor_type: "clients",
        sensor_descr: "Registered SMs",
        sensor_unit: "",
        sensor_divisor: 1
      },
      # 1m Downlink Utilization
      %{
        oid: "1.3.6.1.4.1.161.19.3.1.12.1.1.0",
        sensor_type: "utilization",
        sensor_descr: "1m Downlink Utilization",
        sensor_unit: "%",
        sensor_divisor: 1
      },
      # 1m Uplink Utilization
      %{
        oid: "1.3.6.1.4.1.161.19.3.1.12.1.2.0",
        sensor_type: "utilization",
        sensor_descr: "1m Uplink Utilization",
        sensor_unit: "%",
        sensor_divisor: 1
      },
      # Signal Strength Ratio (SSR)
      %{
        oid: "1.3.6.1.4.1.161.19.3.2.2.108.0",
        sensor_type: "ssr",
        sensor_descr: "Signal Strength Ratio",
        sensor_unit: "dB",
        sensor_divisor: 1
      },
      # CRC Errors
      %{
        oid: "1.3.6.1.4.1.161.19.3.3.1.223.0",
        sensor_type: "errors",
        sensor_descr: "CRC Errors",
        sensor_unit: "",
        sensor_divisor: 1
      },
      # FEC In Errors
      %{
        oid: "1.3.6.1.4.1.161.19.3.3.1.95.0",
        sensor_type: "errors",
        sensor_descr: "FEC In Errors",
        sensor_unit: "",
        sensor_divisor: 1
      },
      # FEC Out Errors
      %{
        oid: "1.3.6.1.4.1.161.19.3.3.1.97.0",
        sensor_type: "errors",
        sensor_descr: "FEC Out Errors",
        sensor_unit: "",
        sensor_divisor: 1
      },
      # GPS Visible Satellites
      %{
        oid: "1.3.6.1.4.1.161.19.3.4.4.7.0",
        sensor_type: "count",
        sensor_descr: "GPS Visible Satellites",
        sensor_unit: "",
        sensor_divisor: 1
      },
      # GPS Tracked Satellites
      %{
        oid: "1.3.6.1.4.1.161.19.3.4.4.8.0",
        sensor_type: "count",
        sensor_descr: "GPS Tracked Satellites",
        sensor_unit: "",
        sensor_divisor: 1
      }
    ]
  end
end
