defmodule Towerops.Gaiia.ClientTest do
  use Towerops.DataCase, async: true

  alias Towerops.Gaiia.Client

  describe "test_connection/2" do
    test "returns ok on successful connection" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "data" => %{
            "accounts" => %{
              "edges" => [],
              "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
            }
          }
        })
      end)

      assert {:ok, %{}} = Client.test_connection("test-key")
    end

    test "returns error on unauthorized" do
      Req.Test.stub(Client, fn conn ->
        conn
        |> Plug.Conn.put_status(401)
        |> Req.Test.json(%{"error" => "unauthorized"})
      end)

      assert {:error, :unauthorized} = Client.test_connection("bad-key")
    end

    test "returns error on forbidden" do
      Req.Test.stub(Client, fn conn ->
        conn
        |> Plug.Conn.put_status(403)
        |> Req.Test.json(%{"error" => "forbidden"})
      end)

      assert {:error, :forbidden} = Client.test_connection("bad-key")
    end
  end

  describe "list_accounts/2" do
    test "returns accounts from single page" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "data" => %{
            "accounts" => %{
              "edges" => [
                %{"node" => %{"id" => "acct-1", "name" => "Alice"}},
                %{"node" => %{"id" => "acct-2", "name" => "Bob"}}
              ],
              "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
            }
          }
        })
      end)

      assert {:ok, accounts} = Client.list_accounts("test-key")
      assert length(accounts) == 2
      assert Enum.map(accounts, & &1["id"]) == ["acct-1", "acct-2"]
    end

    test "handles paginated results" do
      call_count = :counters.new(1, [:atomics])

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

        if count == 1 do
          Req.Test.json(conn, %{
            "data" => %{
              "accounts" => %{
                "edges" => [%{"node" => %{"id" => "acct-1", "name" => "Alice"}}],
                "pageInfo" => %{"hasNextPage" => true, "endCursor" => "cursor-1"}
              }
            }
          })
        else
          Req.Test.json(conn, %{
            "data" => %{
              "accounts" => %{
                "edges" => [%{"node" => %{"id" => "acct-2", "name" => "Bob"}}],
                "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
              }
            }
          })
        end
      end)

      assert {:ok, accounts} = Client.list_accounts("test-key")
      assert length(accounts) == 2
    end

    test "returns error on API failure" do
      Req.Test.stub(Client, fn conn ->
        conn
        |> Plug.Conn.put_status(401)
        |> Req.Test.json(%{"error" => "unauthorized"})
      end)

      assert {:error, :unauthorized} = Client.list_accounts("bad-key")
    end
  end

  describe "list_network_sites/2" do
    test "returns network sites" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "data" => %{
            "networkSites" => %{
              "edges" => [
                %{"node" => %{"id" => "site-1", "name" => "Tower 1"}}
              ],
              "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
            }
          }
        })
      end)

      assert {:ok, sites} = Client.list_network_sites("test-key")
      assert length(sites) == 1
      assert hd(sites)["name"] == "Tower 1"
    end
  end

  describe "list_inventory_items/2" do
    test "returns inventory items" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "data" => %{
            "inventoryItems" => %{
              "edges" => [
                %{"node" => %{"id" => "item-1", "name" => "AF5XHD"}}
              ],
              "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
            }
          }
        })
      end)

      assert {:ok, items} = Client.list_inventory_items("test-key")
      assert length(items) == 1
      assert hd(items)["name"] == "AF5XHD"
    end
  end

  describe "list_billing_subscriptions/2" do
    test "returns billing subscriptions" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "data" => %{
            "billingSubscriptions" => %{
              "edges" => [
                %{
                  "node" => %{
                    "id" => "sub-1",
                    "status" => "Active",
                    "productVersion" => %{"product" => %{"name" => "100Mbps"}}
                  }
                }
              ],
              "pageInfo" => %{"hasNextPage" => false, "endCursor" => nil}
            }
          }
        })
      end)

      assert {:ok, subs} = Client.list_billing_subscriptions("test-key")
      assert length(subs) == 1
    end
  end

  describe "query/4" do
    test "handles GraphQL errors" do
      Req.Test.stub(Client, fn conn ->
        Req.Test.json(conn, %{
          "errors" => [%{"message" => "Field not found"}]
        })
      end)

      assert {:error, {:graphql_errors, _}} = Client.query("test-key", "{ invalid }")
    end

    test "handles rate limiting" do
      Req.Test.stub(Client, fn conn ->
        conn
        |> Plug.Conn.put_resp_header("retry-after", "30")
        |> Plug.Conn.put_status(429)
        |> Req.Test.json(%{"error" => "rate limited"})
      end)

      assert {:error, {:rate_limited, 30}} = Client.query("test-key", "{ viewer { id } }")
    end
  end
end
