"""GitHub Issues operations (#83)."""
from __future__ import annotations

from .core import GitHubError, _graphql


def create_issue(
    token: str,
    repo_id: str,
    title: str,
    body: str,
    label_ids: list[str],
) -> dict:
    """
    Create a GitHub Issue.

    Returns: {"id": str, "number": int, "url": str}
    """
    for name, value in [("repo_id", repo_id), ("title", title), ("body", body)]:
        if not value.strip():
            raise ValueError(f"{name} must not be empty.")

    query = """
    mutation CreateIssue(
      $repositoryId: ID!,
      $title: String!,
      $body: String!,
      $labelIds: [ID!]
    ) {
      createIssue(input: {
        repositoryId: $repositoryId,
        title: $title,
        body: $body,
        labelIds: $labelIds
      }) {
        issue { id number url }
      }
    }
    """
    data = _graphql(
        token,
        query,
        {
            "repositoryId": repo_id,
            "title": title,
            "body": body,
            "labelIds": label_ids,
        },
    )
    issue = data["createIssue"]["issue"]
    return {"id": issue["id"], "number": issue["number"], "url": issue["url"]}


def add_issue_comment(token: str, issue_id: str, body: str) -> dict:
    """Post a comment to a GitHub Issue."""
    if not issue_id.strip():
        raise ValueError("issue_id must not be empty.")
    if not body.strip():
        raise ValueError("body must not be empty.")

    query = """
    mutation AddIssueComment($subjectId: ID!, $body: String!) {
      addComment(input: {subjectId: $subjectId, body: $body}) {
        commentEdge {
          node { id url }
        }
      }
    }
    """
    data = _graphql(token, query, {"subjectId": issue_id, "body": body})
    node = data["addComment"]["commentEdge"]["node"]
    return {"id": node["id"], "url": node["url"]}


def get_issue_body(token: str, issue_id: str) -> str:
    """Fetch the current opening-post body of an Issue.

    Raises GitHubError if the Issue is not found.
    """
    if not issue_id.strip():
        raise ValueError("issue_id must not be empty.")

    query = """
    query($id: ID!) {
      node(id: $id) { ... on Issue { body } }
    }
    """
    data = _graphql(token, query, {"id": issue_id})
    node = data.get("node") or {}
    body = node.get("body")
    if body is None:
        raise GitHubError(f"Issue {issue_id} not found.")
    return body


def update_issue_body(token: str, issue_id: str, body: str) -> bool:
    """Replace the opening post body of an Issue."""
    if not issue_id.strip():
        raise ValueError("issue_id must not be empty.")
    if not body.strip():
        raise ValueError("body must not be empty.")

    query = """
    mutation UpdateIssue($id: ID!, $body: String!) {
      updateIssue(input: {id: $id, body: $body}) {
        issue { id }
      }
    }
    """
    _graphql(token, query, {"id": issue_id, "body": body})
    return True


_STATE_REASON_MAP = {"completed": "COMPLETED", "not_planned": "NOT_PLANNED"}


def close_issue(
    token: str,
    issue_id: str,
    state_reason: str = "completed",
) -> dict:
    """Close an Issue with a state reason (#59).

    state_reason must be "completed" or "not_planned" (mapped to
    GitHub's IssueClosedStateReason enum internally).

    Returns: {"url": str, "closed_at": str}
    """
    if not issue_id.strip():
        raise ValueError("issue_id must not be empty.")
    reason_upper = _STATE_REASON_MAP.get(state_reason)
    if reason_upper is None:
        raise ValueError(
            f"state_reason must be 'completed' or 'not_planned', "
            f"got: {state_reason!r}"
        )

    query = """
    mutation($issueId: ID!, $reason: IssueClosedStateReason) {
      closeIssue(input: {issueId: $issueId, stateReason: $reason}) {
        issue { url closedAt }
      }
    }
    """
    data = _graphql(
        token, query, {"issueId": issue_id, "reason": reason_upper}
    )
    issue = (data.get("closeIssue") or {}).get("issue") or {}
    return {
        "url": issue.get("url", ""),
        "closed_at": issue.get("closedAt", ""),
    }


def get_label_id(token: str, owner: str, repo: str, label_name: str) -> str:
    """Return the node ID for a label, raising GitHubError if not found."""
    if not label_name.strip():
        raise ValueError("label_name must not be empty.")

    query = """
    query GetLabel($owner: String!, $repo: String!, $name: String!) {
      repository(owner: $owner, name: $repo) {
        label(name: $name) { id }
      }
    }
    """
    data = _graphql(token, query, {"owner": owner, "repo": repo, "name": label_name})
    label = data.get("repository", {}).get("label")
    if not label:
        raise GitHubError(
            f"Label '{label_name}' not found in {owner}/{repo}. "
            "Run bootstrap to create standard labels."
        )
    return label["id"]
