defmodule Tarakan.GitHub.GraphQLClientTest do
  use ExUnit.Case, async: false

  import Plug.Conn

  alias Tarakan.GitHub.GraphQLClient

  @node %{
    "id" => "MDEwOlJlcG9zaXRvcnkx",
    "databaseId" => 42,
    "name" => "codex",
    "url" => "https://github.com/openai/codex",
    "isPrivate" => false,
    "isArchived" => false,
    "description" => "Lightweight coding agent",
    "stargazerCount" => 10,
    "forkCount" => 2,
    "owner" => %{"login" => "openai"},
    "defaultBranchRef" => %{"name" => "main"},
    "primaryLanguage" => %{"name" => "TypeScript"}
  }

  setup do
    previous = Application.get_env(:tarakan, :github)

    Application.put_env(:tarakan, :github,
      api_token: "test-token",
      req_options: [plug: {Req.Test, __MODULE__}]
    )

    on_exit(fn ->
      if previous do
        Application.put_env(:tarakan, :github, previous)
      else
        Application.delete_env(:tarakan, :github)
      end
    end)

    :ok
  end

  test "fetches and maps repository metadata" do
    Req.Test.expect(__MODULE__, fn conn ->
      assert conn.method == "POST"
      assert get_req_header(conn, "authorization") == ["Bearer test-token"]

      conn
      |> put_resp_content_type("application/json")
      |> send_resp(200, Jason.encode!(%{"data" => %{"nodes" => [@node]}}))
    end)

    assert {:ok, [metadata]} = GraphQLClient.fetch_repositories_by_node_ids([@node["id"]])
    assert metadata.github_id == 42
    assert metadata.owner == "openai"
    assert metadata.name == "codex"

    Req.Test.verify_on_exit!()
  end

  test "halts the read and rejects responses beyond the 2MB cap" do
    Req.Test.expect(__MODULE__, fn conn ->
      # Not even valid JSON: the cap must trip during the read, before any
      # full-body decode happens.
      send_resp(conn, 200, String.duplicate("x", 2 * 1_024 * 1_024 + 1))
    end)

    assert {:error, :invalid_response} =
             GraphQLClient.fetch_repositories_by_node_ids([@node["id"]])

    Req.Test.verify_on_exit!()
  end

  test "rejects undecodable success bodies" do
    Req.Test.expect(__MODULE__, fn conn ->
      send_resp(conn, 200, "not json")
    end)

    assert {:error, :invalid_response} =
             GraphQLClient.fetch_repositories_by_node_ids([@node["id"]])

    Req.Test.verify_on_exit!()
  end

  test "maps 403 and 429 to rate limiting" do
    Req.Test.expect(__MODULE__, fn conn ->
      send_resp(conn, 429, "slow down")
    end)

    assert {:error, :rate_limited} = GraphQLClient.fetch_repositories_by_node_ids([@node["id"]])

    Req.Test.verify_on_exit!()
  end

  test "rejects batches over 100 node ids before any request" do
    assert {:error, :invalid_response} =
             GraphQLClient.fetch_repositories_by_node_ids(List.duplicate("id", 101))
  end
end
