defmodule Berrypod.Orders.OrderSubmissionWorker do
  @moduledoc """
  Oban worker for submitting paid orders to the fulfilment provider.

  Enqueued after Stripe webhook confirms payment. Guards against
  missing orders, unpaid orders, and already-submitted orders.
  Retries up to 3 times with backoff for transient failures.
  """

  use Oban.Worker, queue: :checkout, max_attempts: 3, unique: [period: 60]

  alias Berrypod.{ActivityLog, Orders}

  require Logger

  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"order_id" => order_id}} = job) do
    case Orders.get_order(order_id) do
      nil ->
        Logger.warning("Order submission: order #{order_id} not found")
        {:cancel, :order_not_found}

      %{payment_status: status} when status != "paid" ->
        Logger.warning("Order submission: order #{order_id} not paid (#{status})")
        {:cancel, :not_paid}

      %{provider_order_id: pid} when not is_nil(pid) ->
        Logger.info("Order submission: order #{order_id} already submitted")
        :ok

      %{shipping_address: addr} when addr == %{} or is_nil(addr) ->
        Logger.warning("Order submission: order #{order_id} has no shipping address, will retry")
        {:error, :no_shipping_address}

      order ->
        case Orders.submit_to_provider(order) do
          {:ok, updated} ->
            Logger.info(
              "Order #{updated.order_number} submitted to provider (#{updated.provider_order_id})"
            )

            ActivityLog.log_event(
              "order.submitted",
              "Submitted to provider (#{updated.provider_order_id})",
              order_id: order.id,
              payload: %{provider_order_id: updated.provider_order_id}
            )

            :ok

          {:error, reason} ->
            Logger.error("Order #{order.order_number} submission failed: #{inspect(reason)}")

            ActivityLog.log_event(
              "order.submission_failed",
              "Submission failed: #{inspect(reason)}",
              level: "error",
              order_id: order.id,
              payload: %{
                error: inspect(reason),
                attempt: job.attempt,
                max_attempts: job.max_attempts
              }
            )

            {:error, reason}
        end
    end
  end

  def enqueue(order_id) do
    %{order_id: order_id}
    |> new()
    |> Oban.insert()
  end
end
