defmodule Berrypod.Clients.PrintifyHttpTest do
  @moduledoc """
  Tests the Printify HTTP client against Req.Test stubs.
  Exercises URL construction, auth headers, response handling, and error cases.
  """

  use ExUnit.Case, async: true

  alias Berrypod.Clients.Printify

  setup do
    Process.put(:printify_api_key, "test_token_printify")
    Req.Test.stub(Printify, &route/1)
    :ok
  end

  # =============================================================================
  # Auth headers
  # =============================================================================

  describe "auth headers" do
    test "includes Bearer token" do
      Req.Test.stub(Printify, fn conn ->
        [auth] = Plug.Conn.get_req_header(conn, "authorization")
        assert auth == "Bearer test_token_printify"
        Req.Test.json(conn, [%{"id" => 1, "title" => "Shop"}])
      end)

      assert {:ok, _} = Printify.get_shops()
    end

    test "includes Content-Type json header" do
      Req.Test.stub(Printify, fn conn ->
        [ct] = Plug.Conn.get_req_header(conn, "content-type")
        assert ct == "application/json"
        Req.Test.json(conn, [])
      end)

      assert {:ok, _} = Printify.get_shops()
    end
  end

  # =============================================================================
  # Error handling
  # =============================================================================

  describe "error handling" do
    test "returns error tuple on 404" do
      Req.Test.stub(Printify, fn conn ->
        Req.Test.json(conn |> Plug.Conn.put_status(404), %{"error" => "Not found"})
      end)

      assert {:error, {404, %{"error" => "Not found"}}} = Printify.get("/shops.json")
    end

    test "returns error tuple on 500" do
      Req.Test.stub(Printify, fn conn ->
        Req.Test.json(conn |> Plug.Conn.put_status(500), %{"error" => "Internal error"})
      end)

      assert {:error, {500, _}} = Printify.get_shops()
    end

    test "post returns error on 422" do
      Req.Test.stub(Printify, fn conn ->
        Req.Test.json(
          conn |> Plug.Conn.put_status(422),
          %{"error" => "Invalid data"}
        )
      end)

      assert {:error, {422, %{"error" => "Invalid data"}}} =
               Printify.create_order("shop1", %{test: true})
    end
  end

  # =============================================================================
  # get_shops/0 and get_shop_id/0
  # =============================================================================

  describe "get_shops/0" do
    test "calls GET /shops.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "GET"
        assert conn.request_path == "/v1/shops.json"
        Req.Test.json(conn, [%{"id" => 42, "title" => "My Shop"}])
      end)

      assert {:ok, [%{"id" => 42}]} = Printify.get_shops()
    end
  end

  describe "get_shop_id/0" do
    test "returns first shop id" do
      assert {:ok, 42} = Printify.get_shop_id()
    end

    test "returns error when no shops" do
      Req.Test.stub(Printify, fn conn ->
        Req.Test.json(conn, [])
      end)

      assert {:error, :no_shops} = Printify.get_shop_id()
    end
  end

  # =============================================================================
  # list_products/2
  # =============================================================================

  describe "list_products/2" do
    test "passes limit and page as query params" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.request_path == "/v1/shops/shop1/products.json"
        assert conn.query_string == "limit=10&page=3"
        Req.Test.json(conn, %{"data" => [], "current_page" => 3, "last_page" => 3})
      end)

      assert {:ok, _} = Printify.list_products("shop1", limit: 10, page: 3)
    end

    test "defaults to limit 50 and page 1" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.query_string == "limit=50&page=1"
        Req.Test.json(conn, %{"data" => []})
      end)

      assert {:ok, _} = Printify.list_products("shop1")
    end
  end

  # =============================================================================
  # get_product/2
  # =============================================================================

  describe "get_product/2" do
    test "calls GET /shops/:shop_id/products/:id.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "GET"
        assert conn.request_path == "/v1/shops/shop1/products/prod1.json"
        Req.Test.json(conn, %{"id" => "prod1", "title" => "T-Shirt"})
      end)

      assert {:ok, %{"id" => "prod1"}} = Printify.get_product("shop1", "prod1")
    end
  end

  # =============================================================================
  # create_order/2
  # =============================================================================

  describe "create_order/2" do
    test "sends POST to /shops/:shop_id/orders.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "POST"
        assert conn.request_path == "/v1/shops/shop1/orders.json"

        {:ok, body, _conn} = Plug.Conn.read_body(conn)
        decoded = Jason.decode!(body)
        assert decoded["external_id"] == "SS-001"

        Req.Test.json(conn, %{"id" => "order_123"})
      end)

      assert {:ok, %{"id" => "order_123"}} =
               Printify.create_order("shop1", %{external_id: "SS-001"})
    end
  end

  # =============================================================================
  # get_order/2
  # =============================================================================

  describe "get_order/2" do
    test "calls GET /shops/:shop_id/orders/:id.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "GET"
        assert conn.request_path == "/v1/shops/shop1/orders/order_123.json"

        Req.Test.json(conn, %{
          "id" => "order_123",
          "status" => "in-production",
          "shipments" => []
        })
      end)

      assert {:ok, %{"status" => "in-production"}} = Printify.get_order("shop1", "order_123")
    end
  end

  # =============================================================================
  # get_shipping/2
  # =============================================================================

  describe "get_shipping/2" do
    test "calls GET /catalog/blueprints/:id/print_providers/:id/shipping.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.request_path ==
                 "/v1/catalog/blueprints/6/print_providers/99/shipping.json"

        Req.Test.json(conn, %{
          "handling_time" => %{"value" => 3, "unit" => "day"},
          "profiles" => []
        })
      end)

      assert {:ok, %{"handling_time" => _}} = Printify.get_shipping(6, 99)
    end
  end

  # =============================================================================
  # Webhooks
  # =============================================================================

  describe "create_webhook/4" do
    test "sends POST to /shops/:shop_id/webhooks.json with topic, url, secret" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "POST"
        assert conn.request_path == "/v1/shops/shop1/webhooks.json"

        {:ok, body, _conn} = Plug.Conn.read_body(conn)
        decoded = Jason.decode!(body)
        assert decoded["topic"] == "product:updated"
        assert decoded["url"] == "https://example.com/webhooks"
        assert decoded["secret"] == "wh_secret"

        Req.Test.json(conn, %{"id" => "wh_1", "topic" => "product:updated"})
      end)

      assert {:ok, %{"id" => "wh_1"}} =
               Printify.create_webhook(
                 "shop1",
                 "https://example.com/webhooks",
                 "product:updated",
                 "wh_secret"
               )
    end
  end

  describe "list_webhooks/1" do
    test "calls GET /shops/:shop_id/webhooks.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "GET"
        assert conn.request_path == "/v1/shops/shop1/webhooks.json"
        Req.Test.json(conn, [%{"id" => "wh_1"}])
      end)

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

  describe "delete_webhook/2" do
    test "calls DELETE /shops/:shop_id/webhooks/:id.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "DELETE"
        assert conn.request_path == "/v1/shops/shop1/webhooks/wh_1.json"
        Plug.Conn.send_resp(conn, 204, "")
      end)

      assert {:ok, nil} = Printify.delete_webhook("shop1", "wh_1")
    end
  end

  # =============================================================================
  # update_product/3 (PUT method)
  # =============================================================================

  describe "update_product/3" do
    test "sends PUT to /shops/:shop_id/products/:id.json" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "PUT"
        assert conn.request_path == "/v1/shops/shop1/products/prod1.json"

        {:ok, body, _conn} = Plug.Conn.read_body(conn)
        decoded = Jason.decode!(body)
        assert decoded["title"] == "Updated Title"

        Req.Test.json(conn, %{"id" => "prod1", "title" => "Updated Title"})
      end)

      assert {:ok, %{"title" => "Updated Title"}} =
               Printify.update_product("shop1", "prod1", %{title: "Updated Title"})
    end
  end

  # =============================================================================
  # delete_product/2
  # =============================================================================

  describe "delete_product/2" do
    test "returns {:ok, nil} on 204" do
      Req.Test.stub(Printify, fn conn ->
        assert conn.method == "DELETE"
        assert conn.request_path == "/v1/shops/shop1/products/prod1.json"
        Plug.Conn.send_resp(conn, 204, "")
      end)

      assert {:ok, nil} = Printify.delete_product("shop1", "prod1")
    end
  end

  # =============================================================================
  # Default stub router
  # =============================================================================

  defp route(%Plug.Conn{method: "GET", request_path: "/v1/shops.json"} = conn) do
    Req.Test.json(conn, [%{"id" => 42, "title" => "Test Shop"}])
  end

  defp route(%Plug.Conn{method: "GET"} = conn) do
    Req.Test.json(conn, %{})
  end

  defp route(%Plug.Conn{method: "POST"} = conn) do
    Req.Test.json(conn, %{})
  end

  defp route(%Plug.Conn{method: "PUT"} = conn) do
    Req.Test.json(conn, %{})
  end

  defp route(%Plug.Conn{method: "DELETE"} = conn) do
    Plug.Conn.send_resp(conn, 204, "")
  end
end
