defmodule Berrypod.Orders.OrderSubmissionWorkerTest do
  use Berrypod.DataCase, async: false

  import Mox
  import Berrypod.OrdersFixtures

  alias Berrypod.Orders
  alias Berrypod.Orders.OrderSubmissionWorker
  alias Berrypod.Providers.MockProvider

  setup :verify_on_exit!

  setup do
    Application.put_env(:berrypod, :provider_modules, %{
      "printify" => MockProvider
    })

    on_exit(fn -> Application.delete_env(:berrypod, :provider_modules) end)
    :ok
  end

  describe "perform/1" do
    test "cancels if order not found" do
      fake_id = Ecto.UUID.generate()

      assert {:cancel, :order_not_found} =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => fake_id}})
    end

    test "cancels if order is not paid" do
      order = order_fixture()
      assert order.payment_status == "pending"

      assert {:cancel, :not_paid} =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})
    end

    test "returns :ok if already submitted" do
      {order, _variant, _product, _conn} = submitted_order_fixture()

      assert :ok =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})
    end

    test "errors if no shipping address (will retry)" do
      order = order_fixture(payment_status: "paid")
      assert order.shipping_address == %{}

      assert {:error, :no_shipping_address} =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})
    end

    test "submits to provider successfully" do
      {order, _variant, _product, _conn} = paid_order_with_products_fixture()

      expect(MockProvider, :submit_order, fn _conn, order_data ->
        assert order_data.order_number == order.order_number
        assert length(order_data.line_items) == 1
        {:ok, %{provider_order_id: "printify_order_123"}}
      end)

      assert :ok =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})

      updated = Orders.get_order(order.id)
      assert updated.fulfilment_status == "submitted"
      assert updated.provider_order_id == "printify_order_123"
      assert updated.submitted_at != nil
    end

    test "sets failed status when provider returns error" do
      {order, _variant, _product, _conn} = paid_order_with_products_fixture()

      expect(MockProvider, :submit_order, fn _conn, _order_data ->
        {:error, {422, %{"message" => "Invalid address"}}}
      end)

      assert {:error, {422, _}} =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})

      updated = Orders.get_order(order.id)
      assert updated.fulfilment_status == "failed"
      assert updated.fulfilment_error =~ "Provider API error (422)"
    end

    test "sets failed status when variant not found in local DB" do
      # Create order with a variant_id that doesn't exist in product variants.
      # Provider connection lookup now goes through the variant's product,
      # so a missing variant causes :variant_not_found at routing time.
      order =
        order_fixture(%{
          variant_id: Ecto.UUID.generate(),
          payment_status: "paid",
          shipping_address: %{
            "name" => "Test",
            "line1" => "1 Street",
            "city" => "London",
            "postal_code" => "SW1",
            "country" => "GB"
          }
        })

      assert {:error, :variant_not_found} =
               OrderSubmissionWorker.perform(%Oban.Job{args: %{"order_id" => order.id}})

      updated = Orders.get_order(order.id)
      assert updated.fulfilment_status == "failed"
      assert updated.fulfilment_error =~ "no longer exists"
    end
  end
end
