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

  use Berrypod.DataCase, async: false

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

  import Berrypod.ProductsFixtures

  setup do
    conn =
      provider_connection_fixture(%{
        provider_type: "printful",
        config: %{"store_id" => "99999", "webhook_secret" => "pf_secret_123"}
      })

    %{provider_conn: conn}
  end

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

  describe "test_connection/1 happy path" do
    test "returns store_id and store_name", %{provider_conn: conn} do
      Req.Test.stub(Client, fn %{request_path: "/v2/stores"} = plug_conn ->
        Req.Test.json(plug_conn, %{
          "data" => [%{"id" => 77777, "name" => "My Print Store"}]
        })
      end)

      assert {:ok, result} = Printful.test_connection(conn)
      assert result.store_id == 77777
      assert result.store_name == "My Print Store"
    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} = Printful.fetch_products(conn)
      assert length(products) == 1

      [product] = products
      assert product.provider_product_id == "456789"
      assert product.title == "PC Man T-Shirt"
      assert product.category == "Apparel"

      # Variants normalised
      assert length(product.variants) == 2
      [v1, v2] = product.variants
      assert v1.title == "Black / S"
      assert v1.price == 1350
      assert v2.title == "Natural / S"

      # Images extracted from preview files (one per unique src URL)
      assert length(product.images) == 2

      # Provider data populated
      assert product.provider_data.catalog_product_id == 71
      assert product.provider_data.blueprint_id == 71
      assert is_list(product.provider_data.options)
    end

    test "paginates when first page returns 20 products", %{provider_conn: conn} do
      call_count = :counters.new(1, [:atomics])

      Req.Test.stub(Client, fn plug_conn ->
        case plug_conn.request_path do
          "/store/products" ->
            :counters.add(call_count, 1, 1)
            page = :counters.get(call_count, 1)

            if page == 1 do
              # First page: exactly 20 products to trigger pagination
              products = for i <- 1..20, do: %{"id" => i, "name" => "Product #{i}"}
              Req.Test.json(plug_conn, %{"code" => 200, "result" => products})
            else
              # Second page: fewer than 20 to stop
              Req.Test.json(plug_conn, %{"code" => 200, "result" => [%{"id" => 21}]})
            end

          "/store/products/" <> _id ->
            Req.Test.json(plug_conn, %{
              "code" => 200,
              "result" => sync_product_detail_response()
            })

          "/v2/catalog-products/" <> _id ->
            Req.Test.json(plug_conn, %{
              "data" => %{"colors" => [%{"name" => "Black", "value" => "#0b0b0b"}]}
            })

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

      assert {:ok, products} = Printful.fetch_products(conn)
      # 20 from page 1 + 1 from page 2 = 21 products
      assert length(products) == 21
    end
  end

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

  describe "submit_order/2 happy path" do
    test "creates and confirms an order", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        case {plug_conn.method, plug_conn.request_path} do
          {"POST", "/v2/orders"} ->
            {:ok, body, _} = Plug.Conn.read_body(plug_conn)
            decoded = Jason.decode!(body)

            # Verify the order payload structure
            assert decoded["external_id"] == "SS-001234"
            assert decoded["shipping"] == "STANDARD"
            assert decoded["recipient"]["email"] == "test@example.com"
            assert length(decoded["items"]) == 1

            Req.Test.json(plug_conn, %{
              "data" => %{"id" => 55555, "status" => "draft"}
            })

          {"POST", "/v2/orders/55555/confirmation"} ->
            Req.Test.json(plug_conn, %{
              "data" => %{"id" => 55555, "status" => "pending"}
            })
        end
      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_variant_id: "5001",
            quantity: 2
          }
        ]
      }

      assert {:ok, %{provider_order_id: "55555"}} = Printful.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 ->
        case plug_conn.request_path do
          "/v2/orders/55555/shipments" ->
            Req.Test.json(plug_conn, %{
              "data" => [
                %{
                  "tracking_number" => "PF-TRACK-001",
                  "tracking_url" => "https://tracking.printful.com/PF-TRACK-001"
                }
              ]
            })

          "/v2/orders/55555" ->
            Req.Test.json(plug_conn, %{
              "data" => %{"id" => 55555, "status" => "fulfilled"}
            })
        end
      end)

      assert {:ok, status} = Printful.get_order_status(conn, "55555")
      assert status.status == "shipped"
      assert status.provider_status == "fulfilled"
      assert status.tracking_number == "PF-TRACK-001"
      assert status.tracking_url == "https://tracking.printful.com/PF-TRACK-001"
    end

    test "returns status with nil tracking when no shipments", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        case plug_conn.request_path do
          "/v2/orders/55555/shipments" ->
            Req.Test.json(plug_conn, %{"data" => []})

          "/v2/orders/55555" ->
            Req.Test.json(plug_conn, %{
              "data" => %{"id" => 55555, "status" => "pending"}
            })
        end
      end)

      assert {:ok, status} = Printful.get_order_status(conn, "55555")
      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 "returns rates for target countries", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        assert plug_conn.request_path == "/v2/shipping-rates"

        Req.Test.json(plug_conn, %{
          "data" => [
            %{
              "shipping" => "STANDARD",
              "rate" => "4.99",
              "currency" => "USD",
              "max_delivery_days" => 7
            }
          ]
        })
      end)

      products = [
        %{
          provider_data: %{
            catalog_product_id: 71,
            catalog_variant_ids: [4011, 4012]
          }
        }
      ]

      assert {:ok, rates} = Printful.fetch_shipping_rates(conn, products)
      # Should get one rate per target country (10 countries)
      assert length(rates) == 10

      [rate | _] = rates
      assert rate.blueprint_id == 71
      assert rate.first_item_cost == 499
      assert rate.currency == "USD"
      assert rate.handling_time_days == 7
    end

    test "handles partial failures gracefully", %{provider_conn: conn} do
      call_count = :counters.new(1, [:atomics])

      Req.Test.stub(Client, fn plug_conn ->
        :counters.add(call_count, 1, 1)
        count = :counters.get(call_count, 1)

        # Fail every 3rd country
        if rem(count, 3) == 0 do
          Req.Test.json(plug_conn |> Plug.Conn.put_status(500), %{"error" => "rate limit"})
        else
          Req.Test.json(plug_conn, %{
            "data" => [
              %{
                "shipping" => "STANDARD",
                "rate" => "3.99",
                "currency" => "USD",
                "max_delivery_days" => 5
              }
            ]
          })
        end
      end)

      products = [
        %{provider_data: %{catalog_product_id: 71, catalog_variant_ids: [4011]}}
      ]

      assert {:ok, rates} = Printful.fetch_shipping_rates(conn, products)
      # Some countries fail, but we still get rates for the rest
      assert length(rates) > 0
      assert length(rates) < 10
    end
  end

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

  describe "register_webhooks/2" do
    test "appends token to webhook URL", %{provider_conn: conn} do
      Req.Test.stub(Client, fn plug_conn ->
        {:ok, body, _} = Plug.Conn.read_body(plug_conn)
        decoded = Jason.decode!(body)

        # Verify the token is appended as a query param
        assert decoded["url"] =~ "token=pf_secret_123"
        assert decoded["url"] =~ "https://example.com/webhooks/printful"
        assert is_list(decoded["events"])
        assert "package_shipped" in decoded["events"]

        Req.Test.json(plug_conn, %{"data" => %{"url" => decoded["url"]}})
      end)

      assert {:ok, _} =
               Printful.register_webhooks(conn, "https://example.com/webhooks/printful")
    end

    test "returns error when no webhook_secret" do
      conn =
        provider_connection_fixture(%{
          provider_type: "printful",
          config: %{"store_id" => "99999"}
        })

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

    test "returns error when no store_id" do
      conn =
        provider_connection_fixture(%{
          provider_type: "printful",
          config: %{"webhook_secret" => "pf_secret_123"}
        })

      assert {:error, :no_store_id} =
               Printful.register_webhooks(conn, "https://example.com/webhooks/printful")
    end
  end

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

  defp route_fetch_products(%{request_path: "/store/products"} = conn) do
    # Single page with one product (fewer than 20 — no pagination)
    Req.Test.json(conn, %{
      "code" => 200,
      "result" => [%{"id" => 456_789, "name" => "PC Man T-Shirt"}]
    })
  end

  defp route_fetch_products(%{request_path: "/store/products/" <> _id} = conn) do
    Req.Test.json(conn, %{
      "code" => 200,
      "result" => sync_product_detail_response()
    })
  end

  defp route_fetch_products(%{request_path: "/v2/catalog-products/" <> _id} = conn) do
    Req.Test.json(conn, %{
      "data" => %{
        "colors" => [
          %{"name" => "Black", "value" => "#0b0b0b"},
          %{"name" => "Natural", "value" => "#F5F5DC"}
        ]
      }
    })
  end

  defp route_fetch_products(conn) do
    Req.Test.json(conn, %{"data" => %{}})
  end

  defp sync_product_detail_response do
    %{
      "sync_product" => %{
        "id" => 456_789,
        "name" => "PC Man T-Shirt",
        "thumbnail_url" => "https://files.cdn.printful.com/thumb.png"
      },
      "sync_variants" => [
        %{
          "id" => 5001,
          "color" => "Black",
          "size" => "S",
          "retail_price" => "13.50",
          "sku" => "PCM-BK-S",
          "synced" => true,
          "availability_status" => "active",
          "variant_id" => 4011,
          "product" => %{
            "product_id" => 71,
            "name" => "Bella+Canvas 3001 Unisex Short Sleeve Jersey T-Shirt"
          },
          "files" => [
            %{
              "type" => "preview",
              "preview_url" => "https://files.cdn.printful.com/preview-black.png"
            },
            %{
              "type" => "default",
              "url" => "https://files.cdn.printful.com/artwork.png"
            }
          ]
        },
        %{
          "id" => 5003,
          "color" => "Natural",
          "size" => "S",
          "retail_price" => "13.50",
          "sku" => "PCM-NT-S",
          "synced" => true,
          "availability_status" => "active",
          "variant_id" => 4013,
          "product" => %{
            "product_id" => 71,
            "name" => "Bella+Canvas 3001 Unisex Short Sleeve Jersey T-Shirt"
          },
          "files" => [
            %{
              "type" => "preview",
              "preview_url" => "https://files.cdn.printful.com/preview-natural.png"
            }
          ]
        }
      ]
    }
  end
end
