defmodule Towerops.Snmp.Profiles.Vendors.Powervault do
  @moduledoc """
  Dell PowerVault storage array SNMP handling with text message parsing.

  Supports Dell PowerVault storage arrays using FCMGMT-MIB.

  Dell PowerVault devices report sensor data as text messages rather than
  structured tables. Messages are in format: "Sensor Name: Value Unit"

  Example messages:
  - "Enclosure Temp: 25 C 77.0F"
  - "12V Rail Voltage: 12.1V"
  - "Battery Current: 0.5A"
  - "Battery Charge: 95%"

  Enterprise OID: 1.3.6.1.4.1.674 (Dell)
  """

  @behaviour Towerops.Snmp.Profiles.Vendors.Vendor

  alias Towerops.Snmp.Client

  # FCMGMT-MIB sensor message OID
  @sensor_message_oid "1.3.6.1.3.94.1.8.1.6"

  @impl true
  def profile_names, do: ["dell-powervault", "powervault"]

  @impl true
  def detect_hardware(_client_opts), do: nil

  @impl true
  def discover_wireless_sensors(_client_opts), do: []

  @impl true
  def wireless_oid_defs, do: []

  @doc """
  Post-processes sensors discovered from FCMGMT-MIB to parse text messages
  into structured sensor data.

  Dell PowerVault reports sensors as text strings rather than numeric OIDs.
  This function walks the connUnitSensorMessage table and parses the text
  to extract temperature, voltage, current, and battery charge sensors.

  Message format: "Sensor Name: Value Unit"
  - Temperature: "25 C 77.0F" (extract Celsius)
  - Voltage: "12.1V"
  - Current: "0.5A"
  - Charge: "95%"
  """
  @spec post_process_sensors([map()], Client.connection_opts()) :: [map()]
  def post_process_sensors(sensors, client_opts) do
    # Walk the sensor message table
    case Client.walk(client_opts, @sensor_message_oid) do
      {:ok, messages} when is_map(messages) and map_size(messages) > 0 ->
        # Convert map to list of %{oid: ..., value: ...} for parsing
        message_list = Enum.map(messages, fn {oid, value} -> %{oid: oid, value: value} end)

        # Parse text messages into sensor data
        parsed_sensors = parse_sensor_messages(message_list)

        # Combine with any existing sensors
        sensors ++ parsed_sensors

      _ ->
        # No PowerVault sensors available, return unchanged
        sensors
    end
  end

  # Parse sensor messages from FCMGMT-MIB walk
  @spec parse_sensor_messages([map()]) :: [map()]
  defp parse_sensor_messages(messages) do
    messages
    |> Enum.map(&parse_single_message/1)
    |> Enum.reject(&is_nil/1)
  end

  # Parse a single sensor message
  @spec parse_single_message(map()) :: map() | nil
  defp parse_single_message(%{oid: oid, value: message}) when is_binary(message) do
    # Extract index from OID (last component)
    index = oid |> String.split(".") |> List.last()

    # Split message by colon to separate name from value
    case String.split(message, ":", parts: 2) do
      [name, value_part] ->
        name = String.trim(name)
        value_part = String.trim(value_part)

        # Try to parse as each sensor type
        parse_temperature(oid, index, name, value_part) ||
          parse_voltage(oid, index, name, value_part) ||
          parse_current(oid, index, name, value_part) ||
          parse_charge(oid, index, name, value_part)

      _ ->
        nil
    end
  end

  defp parse_single_message(_), do: nil

  # Parse temperature sensor: "25 C 77.0F"
  @spec parse_temperature(String.t(), String.t(), String.t(), String.t()) :: map() | nil
  defp parse_temperature(oid, index, name, value_part) do
    if String.contains?(name, "Temp") do
      # Match pattern: "25 C 77.0F"
      case Regex.run(~r/^([0-9]+) C ([0-9]+\.[0-9]+)F$/, value_part) do
        [_, celsius, _fahrenheit] ->
          {temp_value, ""} = Integer.parse(celsius)

          %{
            sensor_type: "temperature",
            sensor_descr: name,
            sensor_unit: "°C",
            sensor_oid: oid,
            sensor_index: "powervault_temp.#{index}",
            last_value: temp_value,
            sensor_divisor: 1,
            metadata: %{vendor: "dell_powervault", message_type: "text_parsed"}
          }

        _ ->
          nil
      end
    end
  end

  # Parse voltage sensor: "12.1V"
  @spec parse_voltage(String.t(), String.t(), String.t(), String.t()) :: map() | nil
  defp parse_voltage(oid, index, name, value_part) do
    if String.contains?(name, "Voltage") do
      # Match pattern: "12.1V"
      case Regex.run(~r/^([0-9]+\.[0-9]+)V$/, value_part) do
        [_, voltage] ->
          {voltage_value, ""} = Float.parse(voltage)

          %{
            sensor_type: "voltage",
            sensor_descr: name,
            sensor_unit: "V",
            sensor_oid: oid,
            sensor_index: "powervault_voltage.#{index}",
            last_value: voltage_value,
            sensor_divisor: 1,
            metadata: %{vendor: "dell_powervault", message_type: "text_parsed"}
          }

        _ ->
          nil
      end
    end
  end

  # Parse current sensor: "0.5A"
  @spec parse_current(String.t(), String.t(), String.t(), String.t()) :: map() | nil
  defp parse_current(oid, index, name, value_part) do
    if String.contains?(name, "Current") do
      # Match pattern: "0.5A"
      case Regex.run(~r/^([0-9]+\.[0-9]+)A$/, value_part) do
        [_, current] ->
          {current_value, ""} = Float.parse(current)

          %{
            sensor_type: "current",
            sensor_descr: name,
            sensor_unit: "A",
            sensor_oid: oid,
            sensor_index: "powervault_current.#{index}",
            last_value: current_value,
            sensor_divisor: 1,
            metadata: %{vendor: "dell_powervault", message_type: "text_parsed"}
          }

        _ ->
          nil
      end
    end
  end

  # Parse battery charge sensor: "95%"
  @spec parse_charge(String.t(), String.t(), String.t(), String.t()) :: map() | nil
  defp parse_charge(oid, index, name, value_part) do
    if String.contains?(name, "Charge") or String.contains?(name, "Battery") do
      # Match pattern: "95%"
      case Regex.run(~r/^([0-9]+)%$/, value_part) do
        [_, charge] ->
          {charge_value, ""} = Integer.parse(charge)

          %{
            sensor_type: "percent",
            sensor_descr: name,
            sensor_unit: "%",
            sensor_oid: oid,
            sensor_index: "powervault_charge.#{index}",
            last_value: charge_value,
            sensor_divisor: 1,
            high_limit: 100,
            low_warn_limit: 20,
            low_limit: 10,
            metadata: %{vendor: "dell_powervault", message_type: "text_parsed"}
          }

        _ ->
          nil
      end
    end
  end
end
