defmodule Towerops.Workers.JobCleanupTask do
  @moduledoc """
  One-time startup task to clean up and reschedule all device polling jobs.

  This task runs on application startup to ensure all polling jobs use the
  latest worker code. This is particularly important when deploying fixes
  that change how jobs are executed (e.g., SNMPv3 credential handling).

  The task is idempotent and safe to run multiple times.
  """

  import Ecto.Query

  alias Towerops.Devices
  alias Towerops.Repo
  alias Towerops.Workers.DeviceMonitorWorker
  alias Towerops.Workers.DevicePollerWorker

  require Logger

  @doc """
  Cancels all existing polling jobs and reschedules them with current code.
  Only runs in production environment.
  """
  def run do
    if Application.get_env(:towerops, :env) == :prod do
      Logger.info("JobCleanupTask: Starting cleanup of polling jobs...")
      cleanup_and_reschedule()
      Logger.info("JobCleanupTask: Cleanup complete")
    else
      Logger.debug("JobCleanupTask: Skipping cleanup in #{Application.get_env(:towerops, :env)} environment")
    end
  end

  defp cleanup_and_reschedule do
    # Cancel all existing polling jobs
    cancel_all_polling_jobs()

    # Wait a moment for cancellations to process
    Process.sleep(1000)

    # Reschedule polling for all enabled devices
    reschedule_all_devices()
  end

  defp cancel_all_polling_jobs do
    {:ok, poller_count} =
      Oban.cancel_all_jobs(
        from(j in Oban.Job,
          where: j.worker == "Towerops.Workers.DevicePollerWorker",
          where: j.state in ["available", "scheduled", "executing", "retryable"]
        )
      )

    {:ok, monitor_count} =
      Oban.cancel_all_jobs(
        from(j in Oban.Job,
          where: j.worker == "Towerops.Workers.DeviceMonitorWorker",
          where: j.state in ["available", "scheduled", "executing", "retryable"]
        )
      )

    Logger.info("JobCleanupTask: Cancelled #{poller_count} polling jobs and #{monitor_count} monitor jobs")
  end

  defp reschedule_all_devices do
    # Get all devices with SNMP enabled
    devices =
      Repo.all(
        from(d in Towerops.Devices.Device,
          where: d.snmp_enabled == true,
          select: d.id
        )
      )

    Logger.info("JobCleanupTask: Rescheduling polling for #{length(devices)} devices")

    # Reschedule each device
    Enum.each(devices, fn device_id ->
      device = Devices.get_device!(device_id)

      # Start polling job
      case DevicePollerWorker.start_polling(device_id) do
        {:ok, _} -> :ok
        {:error, reason} -> Logger.warning("Failed to start polling for device #{device.name}: #{inspect(reason)}")
      end

      # Start monitor job
      case DeviceMonitorWorker.start_monitoring(device_id) do
        {:ok, _} -> :ok
        {:error, reason} -> Logger.warning("Failed to start monitoring for device #{device.name}: #{inspect(reason)}")
      end
    end)
  end
end
