"""Core GraphQL plumbing for active-claude-github-mcp (#83).

`_graphql` and the two error classes live here so every other
github/* module depends only on this leaf and not on sibling modules.
"""
from __future__ import annotations

import json
from typing import Any

import httpx


GITHUB_GRAPHQL_URL = "https://api.github.com/graphql"
_REQUEST_TIMEOUT = 30.0  # seconds


class GitHubError(RuntimeError):
    """
    Raised for any GitHub API failure.
    Includes the full response body for diagnosis.
    """

    def __init__(self, message: str, status_code: int | None = None, body: str = ""):
        super().__init__(message)
        self.status_code = status_code
        self.body = body


class InvalidNodeIdError(Exception):
    """Raised when a caller-supplied node ID does not resolve on GitHub.

    Distinct from GitHubError because the fix is client-side. The tool
    layer must NOT buffer the failing operation — retrying with the
    same bad ID would fail identically and clog the buffer.
    """

    def __init__(self, param_name: str, value: str):
        self.param_name = param_name
        self.value = value
        super().__init__(
            f"{param_name} does not resolve to a reachable GitHub node: `{value}`"
        )


def _graphql(token: str, query: str, variables: dict[str, Any] | None = None) -> dict:
    """
    Execute a GraphQL query/mutation against the GitHub API.

    Raises GitHubError on:
    - HTTP non-200 responses
    - Responses containing a top-level 'errors' key
    - Network timeouts
    """
    if not token:
        raise ValueError("GitHub token must not be empty.")
    if not query.strip():
        raise ValueError("GraphQL query must not be empty.")

    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "Accept": "application/vnd.github+json",
        "X-Github-Next-Global-ID": "1",  # opt into new global node ID format
    }

    payload: dict[str, Any] = {"query": query}
    if variables:
        payload["variables"] = variables

    try:
        response = httpx.post(
            GITHUB_GRAPHQL_URL,
            headers=headers,
            json=payload,
            timeout=_REQUEST_TIMEOUT,
        )
    except httpx.TimeoutException as exc:
        raise GitHubError(
            f"GitHub API request timed out after {_REQUEST_TIMEOUT}s."
        ) from exc
    except httpx.RequestError as exc:
        raise GitHubError(f"GitHub API network error: {exc}") from exc

    if response.status_code != 200:
        raise GitHubError(
            f"GitHub API returned HTTP {response.status_code}.",
            status_code=response.status_code,
            body=response.text,
        )

    data = response.json()

    if "errors" in data:
        errors = json.dumps(data["errors"], indent=2)
        raise GitHubError(
            f"GitHub GraphQL errors:\n{errors}",
            body=response.text,
        )

    return data["data"]
