defmodule Towerops.Workers.MonitorWorker do
  @moduledoc """
  Background worker for device monitoring checks (ICMP pings).

  Enqueued when:
  - trigger_check is called but the DeviceMonitor GenServer doesn't exist
  - Manual monitoring check trigger from UI

  Queue: monitoring
  """

  alias Towerops.Devices
  alias Towerops.Monitoring

  require Logger

  @doc """
  Performs a monitoring check for a device.

  ## Parameters
  - device_id: The UUID of the device to check

  ## Returns
  - :ok on success
  - {:error, reason} on failure
  """
  def perform(device_id) do
    Logger.info("Starting monitoring check for device #{device_id}")

    device = Devices.get_device!(device_id)

    if device.monitoring_enabled do
      case ping_device(device) do
        {:ok, latency} ->
          Logger.debug("Device #{device_id} is up, latency: #{latency}ms")

          Monitoring.create_check(%{
            device_id: device_id,
            status: :success,
            response_time_ms: latency,
            checked_at: DateTime.utc_now()
          })

          # Broadcast monitoring check update to device-specific topic
          Phoenix.PubSub.broadcast(
            Towerops.PubSub,
            "device:#{device_id}",
            {:monitoring_check_updated, device_id}
          )

          :ok

        {:error, reason} ->
          Logger.warning("Device #{device_id} is down: #{inspect(reason)}")

          Monitoring.create_check(%{
            device_id: device_id,
            status: :failure,
            response_time_ms: nil,
            checked_at: DateTime.utc_now()
          })

          # Broadcast monitoring check update to device-specific topic
          Phoenix.PubSub.broadcast(
            Towerops.PubSub,
            "device:#{device_id}",
            {:monitoring_check_updated, device_id}
          )

          :ok
      end
    else
      Logger.debug("Device #{device_id} does not have monitoring enabled, skipping check")
      :ok
    end
  rescue
    Ecto.NoResultsError ->
      Logger.error("Device #{device_id} not found")
      {:error, :device_not_found}

    error ->
      Logger.error("Monitoring check failed for device #{device_id}: #{inspect(error)}")
      {:error, error}
  end

  defp ping_device(device) do
    # Use system ping command (works on most Unix-like systems)
    case System.cmd("ping", ["-c", "1", "-W", "1", device.ip_address], stderr_to_stdout: true) do
      {output, 0} ->
        # Extract latency from ping output
        latency = extract_latency(output)
        {:ok, latency}

      {_output, _status} ->
        {:error, :timeout}
    end
  rescue
    error ->
      {:error, error}
  end

  defp extract_latency(output) do
    # Parse ping output to extract latency
    # Example: "time=1.234 ms"
    case Regex.run(~r/time=([\d.]+)/, output) do
      [_, latency_str] ->
        case Float.parse(latency_str) do
          {latency, _} -> latency
          :error -> 0.0
        end

      nil ->
        0.0
    end
  end
end
