defmodule Berrypod.Providers.PrintifyIntegrationTest do
  @moduledoc """
  Happy-path integration tests for the Printify provider.
  Uses Req.Test to stub HTTP responses and a real DB connection for fixtures.
  """

  use Berrypod.DataCase, async: false

  alias Berrypod.Clients.Printify, as: Client
  alias Berrypod.Providers.Printify

  import Berrypod.ProductsFixtures

  setup do
    conn =
      provider_connection_fixture(%{
        provider_type: "printify",
        config: %{"shop_id" => "12345", "webhook_secret" => "py_secret_abc"}
      })

    %{provider_conn: conn}
  end

  # =============================================================================
  # test_connection/1
  # =============================================================================

  describe "test_connection/1 happy path" do
    test "returns shop_id and shop_name", %{provider_conn: conn} do
      Req.Test.stub(Client, fn %{request_path: "/v1/shops.json"} = plug_conn ->
        Req.Test.json(plug_conn, [
          %{"id" => 55555, "title" => "Berry Pod Shop"}
        ])
      end)

      assert {:ok, result} = Printify.test_connection(conn)
      assert result.shop_id == 55555
      assert result.shop_name == "Berry Pod Shop"
    end
  end

  # =============================================================================
  # fetch_products/1
  # =============================================================================

  describe "fetch_products/1 happy path" do
    test "fetches and normalizes a single page of products", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        route_fetch_products(plug_conn)
      end)

      assert {:ok, products} = Printify.fetch_products(conn)
      assert length(products) == 1

      [product] = products
      assert product.provider_product_id == "12345"
      assert product.title == "Classic T-Shirt"
      assert product.category == "Apparel"

      # Variants normalised
      assert length(product.variants) == 2
      [v1, v2] = product.variants
      assert v1.provider_variant_id == "100"
      assert v1.title == "Solid White / S"
      assert v1.price == 2500
      assert v2.provider_variant_id == "101"

      # Images present
      assert length(product.images) >= 1

      # Provider data populated
      assert product.provider_data.blueprint_id == 145
      assert product.provider_data.print_provider_id == 29
      assert is_list(product.provider_data.options)
    end

    test "paginates when current_page < last_page", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        case plug_conn.request_path do
          "/v1/shops/12345/products.json" ->
            # Parse page from query string
            params = URI.decode_query(plug_conn.query_string || "")
            page = String.to_integer(params["page"] || "1")

            if page == 1 do
              Req.Test.json(plug_conn, %{
                "current_page" => 1,
                "last_page" => 2,
                "data" => [printify_product_response()]
              })
            else
              Req.Test.json(plug_conn, %{
                "current_page" => 2,
                "last_page" => 2,
                "data" => [
                  printify_product_response()
                  |> Map.put("id", "67890")
                  |> Map.put("title", "Second Product")
                ]
              })
            end

          _ ->
            Req.Test.json(plug_conn, %{})
        end
      end)

      assert {:ok, products} = Printify.fetch_products(conn)
      assert length(products) == 2
    end
  end

  # =============================================================================
  # submit_order/2
  # =============================================================================

  describe "submit_order/2 happy path" do
    test "creates an order and returns provider_order_id", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.method == "POST"
        assert plug_conn.request_path == "/v1/shops/12345/orders.json"

        {:ok, body, _} = Plug.Conn.read_body(plug_conn)
        decoded = Jason.decode!(body)

        assert decoded["external_id"] == "SS-001234"
        assert decoded["address_to"]["first_name"] == "Jane"
        assert decoded["address_to"]["country"] == "GB"
        assert length(decoded["line_items"]) == 1

        Req.Test.json(plug_conn, %{"id" => "printify_order_555"})
      end)

      order_data = %{
        order_number: "SS-001234",
        customer_email: "test@example.com",
        shipping_address: %{
          "name" => "Jane Doe",
          "line1" => "1 High Street",
          "city" => "London",
          "country" => "GB",
          "postal_code" => "SW1A 1AA"
        },
        line_items: [
          %{
            provider_product_id: "prod_1",
            provider_variant_id: "100",
            quantity: 2
          }
        ]
      }

      assert {:ok, %{provider_order_id: "printify_order_555"}} =
               Printify.submit_order(conn, order_data)
    end
  end

  # =============================================================================
  # get_order_status/2
  # =============================================================================

  describe "get_order_status/2 happy path" do
    test "returns normalised status with tracking", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.request_path == "/v1/shops/12345/orders/order_555.json"

        Req.Test.json(plug_conn, %{
          "id" => "order_555",
          "status" => "shipped",
          "shipments" => [
            %{
              "tracking_number" => "PY-TRACK-001",
              "tracking_url" => "https://tracking.printify.com/PY-TRACK-001"
            }
          ]
        })
      end)

      assert {:ok, status} = Printify.get_order_status(conn, "order_555")
      assert status.status == "shipped"
      assert status.provider_status == "shipped"
      assert status.tracking_number == "PY-TRACK-001"
      assert status.tracking_url == "https://tracking.printify.com/PY-TRACK-001"
    end

    test "returns status with nil tracking when no shipments", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        Req.Test.json(plug_conn, %{
          "id" => "order_555",
          "status" => "pending",
          "shipments" => []
        })
      end)

      assert {:ok, status} = Printify.get_order_status(conn, "order_555")
      assert status.status == "submitted"
      assert is_nil(status.tracking_number)
    end
  end

  # =============================================================================
  # fetch_shipping_rates/2
  # =============================================================================

  describe "fetch_shipping_rates/2 happy path" do
    test "normalises profiles into per-country rates", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.request_path =~
                 "/v1/catalog/blueprints/145/print_providers/29/shipping.json"

        Req.Test.json(plug_conn, %{
          "handling_time" => %{"value" => 5, "unit" => "day"},
          "profiles" => [
            %{
              "variant_ids" => [100, 101],
              "first_item" => %{"cost" => 399, "currency" => "USD"},
              "additional_items" => %{"cost" => 199, "currency" => "USD"},
              "countries" => ["US", "GB", "CA"]
            },
            %{
              "variant_ids" => [100, 101],
              "first_item" => %{"cost" => 599, "currency" => "USD"},
              "additional_items" => %{"cost" => 299, "currency" => "USD"},
              "countries" => ["AU", "NZ"]
            }
          ]
        })
      end)

      products = [
        %{
          provider_data: %{
            blueprint_id: 145,
            print_provider_id: 29
          }
        }
      ]

      assert {:ok, rates} = Printify.fetch_shipping_rates(conn, products)
      # 3 countries from profile 1 + 2 from profile 2 = 5
      assert length(rates) == 5

      us_rate = Enum.find(rates, &(&1.country_code == "US"))
      assert us_rate.blueprint_id == 145
      assert us_rate.first_item_cost == 399
      assert us_rate.additional_item_cost == 199
      assert us_rate.currency == "USD"
      assert us_rate.handling_time_days == 5

      au_rate = Enum.find(rates, &(&1.country_code == "AU"))
      assert au_rate.first_item_cost == 599
    end

    test "handles API errors gracefully", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        Req.Test.json(plug_conn |> Plug.Conn.put_status(500), %{"error" => "rate limit"})
      end)

      products = [
        %{provider_data: %{blueprint_id: 145, print_provider_id: 29}}
      ]

      # Should still return ok with empty rates (logged warning)
      assert {:ok, []} = Printify.fetch_shipping_rates(conn, products)
    end
  end

  # =============================================================================
  # register_webhooks/2
  # =============================================================================

  describe "register_webhooks/2" do
    test "creates a webhook for each event", %{provider_conn: conn} do
      events_received = :counters.new(1, [:atomics])

      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.method == "POST"
        assert plug_conn.request_path == "/v1/shops/12345/webhooks.json"

        {:ok, body, _} = Plug.Conn.read_body(plug_conn)
        decoded = Jason.decode!(body)
        assert is_binary(decoded["topic"])
        assert decoded["url"] =~ "https://example.com/webhooks"
        assert decoded["secret"] == "py_secret_abc"

        :counters.add(events_received, 1, 1)
        Req.Test.json(plug_conn, %{"id" => "wh_#{:counters.get(events_received, 1)}"})
      end)

      assert {:ok, results} = Printify.register_webhooks(conn, "https://example.com/webhooks")
      assert length(results) == 6

      Enum.each(results, fn result ->
        assert {:ok, _event, _response} = result
      end)
    end

    test "returns error when no webhook_secret" do
      conn =
        provider_connection_fixture(%{
          provider_type: "printify",
          config: %{"shop_id" => "12345"}
        })

      assert {:error, :no_webhook_secret} =
               Printify.register_webhooks(conn, "https://example.com/webhooks")
    end

    test "returns error when no shop_id" do
      conn =
        provider_connection_fixture(%{
          provider_type: "printify",
          config: %{"webhook_secret" => "py_secret_abc"}
        })

      assert {:error, :no_shop_id} =
               Printify.register_webhooks(conn, "https://example.com/webhooks")
    end
  end

  # =============================================================================
  # list_webhooks/1
  # =============================================================================

  describe "list_webhooks/1" do
    test "returns webhooks for the shop", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.request_path == "/v1/shops/12345/webhooks.json"
        Req.Test.json(plug_conn, [%{"id" => "wh_1", "topic" => "product:updated"}])
      end)

      assert {:ok, [%{"id" => "wh_1"}]} = Printify.list_webhooks(conn)
    end
  end

  # =============================================================================
  # Stub responses
  # =============================================================================

  defp route_fetch_products(%{request_path: "/v1/shops/12345/products.json"} = conn) do
    Req.Test.json(conn, %{
      "current_page" => 1,
      "last_page" => 1,
      "data" => [printify_product_response()]
    })
  end

  defp route_fetch_products(conn) do
    Req.Test.json(conn, %{})
  end
end
