defmodule Towerops.Snmp.NeighborCleanupWorker do
  @moduledoc """
  Oban worker that periodically cleans up stale neighbor, ARP, and MAC FDB records.

  Neighbors, ARP entries, and MAC forwarding database entries that haven't been
  seen in the last 24 hours are considered stale and will be automatically
  removed from the database.

  Runs hourly via Oban.Plugins.Cron (configured in application config).
  """
  use Oban.Worker, queue: :maintenance

  alias Towerops.Devices
  alias Towerops.Snmp
  alias Towerops.Topology

  require Logger

  # Consider neighbors stale if not seen in 24 hours
  @stale_threshold_hours 24

  @impl Oban.Worker
  @spec perform(Oban.Job.t()) :: :ok
  def perform(%Oban.Job{}) do
    cleanup_stale_records()
    cleanup_stale_topology_links()
    :ok
  end

  defp cleanup_stale_records do
    cutoff = DateTime.add(DateTime.utc_now(), -@stale_threshold_hours, :hour)

    Logger.info("Starting neighbor, ARP, and MAC cleanup for records older than #{cutoff}")

    # device IDs with SNMP enabled
    equipment_list = Devices.list_snmp_enabled_devices()

    {total_neighbors, total_arp, total_mac} =
      Enum.reduce(equipment_list, {0, 0, 0}, fn device, {neighbor_acc, arp_acc, mac_acc} ->
        neighbors_deleted = delete_stale_records(device, cutoff, &Snmp.delete_stale_neighbors/2, "neighbors")
        arp_deleted = delete_stale_records(device, cutoff, &Snmp.delete_stale_arp_entries/2, "ARP entries")
        mac_deleted = delete_stale_records(device, cutoff, &Snmp.delete_stale_mac_addresses/2, "MAC entries")

        {neighbor_acc + neighbors_deleted, arp_acc + arp_deleted, mac_acc + mac_deleted}
      end)

    log_cleanup_results(total_neighbors, total_arp, total_mac)
  end

  defp delete_stale_records(device, cutoff, delete_fn, record_type) do
    case delete_fn.(device.id, cutoff) do
      {count, _} when count > 0 ->
        Logger.info("Deleted #{count} stale #{record_type} for device #{device.name} (#{device.id})")
        count

      _ ->
        0
    end
  end

  defp cleanup_stale_topology_links do
    {stale_count, deleted_count} = Topology.cleanup_stale_links()

    if stale_count > 0 or deleted_count > 0 do
      Logger.info("Topology link cleanup: marked #{stale_count} stale, deleted #{deleted_count}")
    end
  end

  defp log_cleanup_results(total_neighbors, total_arp, total_mac) do
    if total_neighbors > 0 or total_arp > 0 or total_mac > 0 do
      Logger.info(
        "Cleanup completed: removed #{total_neighbors} neighbors, #{total_arp} ARP entries, #{total_mac} MAC entries"
      )
    else
      Logger.debug("Cleanup completed: no stale records found")
    end

    :ok
  end
end
