defmodule Towerops.Workers.PreseemSyncWorker do
  @moduledoc """
  Oban worker that syncs Preseem data for enabled integrations.

  The cron job (no args) runs every 10 minutes and dispatches individual
  per-integration sync jobs staggered across the 600-second window using
  `PollingOffset.calculate_offset/2`. This prevents a thundering herd
  against the Preseem API.

  Individual jobs receive `%{"integration_id" => id}` and perform the
  actual sync via `Preseem.Sync.sync_organization/1`.
  """
  use Oban.Worker, queue: :maintenance

  alias Towerops.Integrations
  alias Towerops.Preseem.Sync
  alias Towerops.Workers.PollingOffset

  require Logger

  @window_seconds 600

  # Cron dispatcher: enqueues staggered individual sync jobs
  @impl Oban.Worker
  def perform(%Oban.Job{args: args}) when args == %{} do
    integrations = Integrations.list_enabled_integrations("preseem")

    eligible = Enum.filter(integrations, &should_sync?/1)

    now = DateTime.utc_now()

    enqueued =
      Enum.count(eligible, fn integration ->
        offset = PollingOffset.calculate_offset(integration.organization_id, @window_seconds)
        scheduled_at = DateTime.add(now, offset, :second)

        case %{"integration_id" => integration.id}
             |> new(scheduled_at: scheduled_at, unique: [period: @window_seconds])
             |> Oban.insert() do
          {:ok, _job} -> true
          {:error, _reason} -> false
        end
      end)

    if enqueued > 0 do
      Logger.info("Preseem sync: dispatched #{enqueued} jobs across #{@window_seconds}s window")
    end

    :ok
  end

  # Individual sync: loads integration by ID and syncs
  def perform(%Oban.Job{args: %{"integration_id" => id}}) do
    case Integrations.get_integration_by_id(id) do
      {:ok, integration} ->
        sync_integration(integration)

      {:error, :not_found} ->
        Logger.warning("Preseem sync: integration #{id} not found, skipping")
        {:error, :not_found}
    end
  end

  defp sync_integration(integration) do
    case Sync.sync_organization(integration) do
      {:ok, result} ->
        Logger.info("Preseem sync completed for org #{integration.organization_id}: #{inspect(result)}")

        :ok

      {:error, reason} ->
        Logger.error("Preseem sync failed for org #{integration.organization_id}: #{inspect(reason)}")

        :ok
    end
  end

  defp should_sync?(integration) do
    case integration.last_synced_at do
      nil ->
        true

      last_synced_at ->
        interval_seconds = (integration.sync_interval_minutes || 10) * 60
        elapsed = DateTime.diff(DateTime.utc_now(), last_synced_at, :second)
        elapsed >= interval_seconds
    end
  end
end
