defmodule Towerops.Snmp.MibTranslator do
  @moduledoc """
  Translates SNMP MIB symbolic names to numeric OIDs using net-snmp's snmptranslate.

  Uses the system's snmptranslate command to resolve MIB names like
  "MIKROTIK-MIB::mtxrSerialNumber.0" to numeric OIDs like "1.3.6.1.4.1.14988.1.1.7.3.0".

  Requires net-snmp package to be installed and MIB files to be available.
  """

  require Logger

  @doc """
  Translates a MIB symbolic name to a numeric OID.

  ## Examples

      iex> translate("MIKROTIK-MIB::mtxrSerialNumber.0")
      {:ok, "1.3.6.1.4.1.14988.1.1.7.3.0"}

      iex> translate("SNMPv2-MIB::sysDescr.0")
      {:ok, "1.3.6.1.2.1.1.1.0"}

      iex> translate("1.3.6.1.2.1.1.1.0")
      {:ok, "1.3.6.1.2.1.1.1.0"}

      iex> translate("INVALID-MIB::badObject")
      {:error, :translation_failed}
  """
  @spec translate(String.t()) :: {:ok, String.t()} | {:error, :translation_failed}
  def translate(mib_name) when is_binary(mib_name) do
    # If it's already a numeric OID, return it as-is
    if numeric_oid?(mib_name) do
      {:ok, mib_name}
    else
      translate_mib_name(mib_name)
    end
  end

  @doc """
  Translates multiple MIB names to numeric OIDs in a single batch.

  Returns a map of mib_name => {:ok, oid} or {:error, reason}.
  """
  @spec translate_batch([String.t()]) :: %{String.t() => {:ok, String.t()} | {:error, term()}}
  def translate_batch(mib_names) when is_list(mib_names) do
    Map.new(mib_names, fn name ->
      {name, translate(name)}
    end)
  end

  # Private functions

  defp numeric_oid?(string) do
    String.match?(string, ~r/^\.?\d+(\.\d+)*$/)
  end

  defp translate_mib_name(mib_name) do
    # Get MIB directories from config or use default
    mib_dirs = Application.get_env(:towerops, :mib_dirs, ["/app/mibs", "/usr/share/snmp/mibs"])
    mib_dir_args = Enum.flat_map(mib_dirs, fn dir -> ["-M", "+#{dir}"] end)

    # Use snmptranslate to convert MIB name to numeric OID
    # -On: Output numeric OID
    # -IR: Random access to MIB registry
    args = ["-On", "-IR"] ++ mib_dir_args ++ [mib_name]

    case System.cmd("snmptranslate", args, stderr_to_stdout: true) do
      {output, 0} ->
        # snmptranslate returns the numeric OID, sometimes with a leading dot
        oid = String.trim(output)
        {:ok, oid}

      {error_output, _exit_code} ->
        Logger.warning("Failed to translate MIB name #{mib_name}: #{error_output}")
        {:error, :translation_failed}
    end
  rescue
    e in ErlangError ->
      # snmptranslate command not found or other system error
      Logger.error("snmptranslate command failed: #{inspect(e)}")
      {:error, :command_not_found}
  end
end
