defmodule Towerops.Snmp.Profiles.Vendors.Edgeswitch do
  @moduledoc """
  Ubiquiti EdgeSwitch device-specific SNMP handling.

  Supports EdgeSwitch and EdgePoint switch products with temperature,
  fan speed, and state sensors from the EdgeSwitch-BOXSERVICES-PRIVATE-MIB.

  Supported devices:
  - EdgeSwitch series (ES-8, ES-16, ES-24, ES-48, etc.)
  - EdgePoint Switch series
  - UniFi Switch (USW) series
  """

  @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

  alias Towerops.Snmp.Client

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

  @impl true
  def detect_hardware(client_opts) do
    # EdgeSwitch uses sysDescr for hardware model detection
    # Pattern: EdgeSwitch 24-250W, firmware 1.9.1.5274897, Linux 4.4.153
    case Client.get(client_opts, "1.3.6.1.2.1.1.1.0") do
      {:ok, descr} when is_binary(descr) and descr != "" ->
        parse_hardware_from_descr(descr)

      _ ->
        nil
    end
  end

  # Extract hardware model from sysDescr
  # Examples:
  # "EdgeSwitch 24-250W, firmware 1.9.1.5274897, Linux 4.4.153"
  # "EdgePoint Switch ES-8XP, firmware 1.7.3.4969649, Linux 3.6.5"
  # "USW-48-PoE, firmware 5.43.35.12724, Linux 4.9.147"
  defp parse_hardware_from_descr(descr) do
    # Match everything before the first comma (hardware model)
    case Regex.run(~r/^([^,]+)/, descr) do
      [_, hardware] -> String.trim(hardware)
      _ -> nil
    end
  end

  @impl true
  def wireless_oid_defs do
    [
      # Temperature sensors (from YAML)
      %{
        oid: "1.3.6.1.4.1.4413.1.1.43.1.8.1.5",
        sensor_type: "temperature",
        sensor_descr: "Temperature",
        sensor_unit: "C",
        sensor_divisor: 1
      },
      # Fan speed sensors (from YAML)
      %{
        oid: "1.3.6.1.4.1.4413.1.1.43.1.6.1.4",
        sensor_type: "fanspeed",
        sensor_descr: "Fan",
        sensor_unit: "RPM",
        sensor_divisor: 1
      }
    ]
  end

  @impl true
  def discover_wireless_sensors(client_opts) do
    # EdgeSwitch has state sensors, temperature, and fan speed sensors
    # These are NOT wireless sensors, but the framework uses this method
    # for vendor-specific sensor discovery

    static_sensors = discover_static_sensors(client_opts)
    state_sensors = discover_state_sensors(client_opts)

    static_sensors ++ state_sensors
  end

  # Discover temperature and fan speed sensors using table walks
  @spec discover_static_sensors(Client.connection_opts()) :: [map()]
  defp discover_static_sensors(client_opts) do
    temp_sensors = discover_temperature_sensors(client_opts)
    fan_sensors = discover_fan_speed_sensors(client_opts)

    temp_sensors ++ fan_sensors
  end

  # Discover temperature sensors from boxServicesTempSensorsEntry
  @spec discover_temperature_sensors(Client.connection_opts()) :: [map()]
  defp discover_temperature_sensors(client_opts) do
    case Client.walk(client_opts, "1.3.6.1.4.1.4413.1.1.43.1.8.1.5") do
      {:ok, temperatures} when is_map(temperatures) and map_size(temperatures) > 0 ->
        Enum.map(temperatures, fn {oid, temp} ->
          index = oid |> String.split(".") |> List.last()

          %{
            oid: oid,
            sensor_type: "temperature",
            sensor_descr: "Temperature #{index}",
            sensor_unit: "C",
            sensor_divisor: 1,
            sensor_index: "edgeswitch_temp_#{index}",
            last_value: temp
          }
        end)

      _ ->
        []
    end
  end

  # Discover fan speed sensors from boxServicesFansEntry
  @spec discover_fan_speed_sensors(Client.connection_opts()) :: [map()]
  defp discover_fan_speed_sensors(client_opts) do
    case Client.walk(client_opts, "1.3.6.1.4.1.4413.1.1.43.1.6.1.4") do
      {:ok, fan_speeds} when is_map(fan_speeds) and map_size(fan_speeds) > 0 ->
        Enum.map(fan_speeds, fn {oid, speed} ->
          index = oid |> String.split(".") |> List.last()

          %{
            oid: oid,
            sensor_type: "fanspeed",
            sensor_descr: "Fan #{index}",
            sensor_unit: "RPM",
            sensor_divisor: 1,
            sensor_index: "edgeswitch_fan_#{index}",
            last_value: speed
          }
        end)

      _ ->
        []
    end
  end

  # Discover state sensors (chassis state, temp sensor state, fan state)
  @spec discover_state_sensors(Client.connection_opts()) :: [map()]
  defp discover_state_sensors(client_opts) do
    chassis_state = discover_chassis_state(client_opts)
    temp_state = discover_temp_sensor_state(client_opts)
    fan_state = discover_fan_state(client_opts)

    chassis_state ++ temp_state ++ fan_state
  end

  # Discover chassis state from boxServicesTempUnitEntry
  @spec discover_chassis_state(Client.connection_opts()) :: [map()]
  defp discover_chassis_state(client_opts) do
    case Client.walk(client_opts, "1.3.6.1.4.1.4413.1.1.43.1.15.1.2") do
      {:ok, states} when is_map(states) and map_size(states) > 0 ->
        Enum.map(states, fn {oid, state} ->
          index = oid |> String.split(".") |> List.last()

          %{
            oid: oid,
            sensor_type: "state",
            sensor_descr: "Chassis state",
            sensor_unit: "",
            sensor_divisor: 1,
            sensor_index: "edgeswitch_chassis_state_#{index}",
            last_value: state
          }
        end)

      _ ->
        []
    end
  end

  # Discover temperature sensor state from boxServicesTempSensorsEntry
  @spec discover_temp_sensor_state(Client.connection_opts()) :: [map()]
  defp discover_temp_sensor_state(client_opts) do
    case Client.walk(client_opts, "1.3.6.1.4.1.4413.1.1.43.1.8.1.4") do
      {:ok, states} when is_map(states) and map_size(states) > 0 ->
        Enum.map(states, fn {oid, state} ->
          index = oid |> String.split(".") |> List.last()

          %{
            oid: oid,
            sensor_type: "state",
            sensor_descr: "Temp Sensor #{index}",
            sensor_unit: "",
            sensor_divisor: 1,
            sensor_index: "edgeswitch_temp_state_#{index}",
            last_value: state
          }
        end)

      _ ->
        []
    end
  end

  # Discover fan state from boxServicesFansEntry
  @spec discover_fan_state(Client.connection_opts()) :: [map()]
  defp discover_fan_state(client_opts) do
    case Client.walk(client_opts, "1.3.6.1.4.1.4413.1.1.43.1.6.1.3") do
      {:ok, states} when is_map(states) and map_size(states) > 0 ->
        Enum.map(states, fn {oid, state} ->
          index = oid |> String.split(".") |> List.last()

          %{
            oid: oid,
            sensor_type: "state",
            sensor_descr: "Fan #{index}",
            sensor_unit: "",
            sensor_divisor: 1,
            sensor_index: "edgeswitch_fan_state_#{index}",
            last_value: state
          }
        end)

      _ ->
        []
    end
  end
end
