# Copyright (C) 2026 GoodData Corporation

"""List review threads for a pull request."""

from __future__ import annotations

from typing import cast

from aida_mcp.tools.github.client import GithubApiError, github_post_graphql
from aida_mcp.tools.github.comments_types import ListThreadsResult, ReviewThread, ThreadComment


async def list_review_threads(
    *, repo: str, pr_number: int, limit: int = 30, include_resolved: bool = False
) -> ListThreadsResult:
    owner, name = repo.split("/", 1)

    query = """
    query($owner: String!, $name: String!, $pr: Int!, $after: String) {
      repository(owner: $owner, name: $name) {
        pullRequest(number: $pr) {
          reviewThreads(first: 50, after: $after) {
            pageInfo { hasNextPage endCursor }
            nodes {
              id
              isResolved
              path
              line
              diffSide
              comments(first: 10) {
                nodes {
                  url
                  body
                  createdAt
                  author { login }
                  diffHunk
                }
              }
            }
          }
        }
      }
    }
    """

    threads_out: list[ReviewThread] = []
    after: str | None = None
    for _ in range(10):
        data_obj = await github_post_graphql(
            query=query,
            variables={"owner": owner, "name": name, "pr": pr_number, "after": after},
        )
        if not isinstance(data_obj, dict):
            raise GithubApiError("Unexpected GraphQL response shape.")
        doc = cast(dict[str, object], data_obj)
        data_field = doc.get("data")
        if not isinstance(data_field, dict):
            raise GithubApiError("Unexpected GraphQL response shape (missing data).")
        data = cast(dict[str, object], data_field)
        repo_field_obj = data.get("repository")
        pr_field_obj: object | None = None
        if isinstance(repo_field_obj, dict):
            repo_field = cast(dict[str, object], repo_field_obj)
            pr_field_obj = repo_field.get("pullRequest")

        rt_field_obj: object | None = None
        if isinstance(pr_field_obj, dict):
            pr_field = cast(dict[str, object], pr_field_obj)
            rt_field_obj = pr_field.get("reviewThreads")

        nodes_obj: object = []
        page_info_obj: object | None = None
        if isinstance(rt_field_obj, dict):
            rt_field = cast(dict[str, object], rt_field_obj)
            nodes_obj = rt_field.get("nodes") or []
            page_info_obj = rt_field.get("pageInfo")

        if isinstance(nodes_obj, list):
            nodes = cast(list[object], nodes_obj)
            for t_obj in nodes:
                if not isinstance(t_obj, dict):
                    continue
                t = cast(dict[str, object], t_obj)
                is_resolved = t.get("isResolved") is True
                if is_resolved and not include_resolved:
                    continue
                tid = t.get("id")
                if not isinstance(tid, str) or not tid:
                    continue

                # Extract thread-level fields
                path_raw = t.get("path")
                path = path_raw if isinstance(path_raw, str) else ""
                line_raw = t.get("line")
                line = line_raw if isinstance(line_raw, int) else None

                comments_obj = t.get("comments")
                comment_nodes_obj: object = []
                if isinstance(comments_obj, dict):
                    comments = cast(dict[str, object], comments_obj)
                    comment_nodes_obj = comments.get("nodes") or []

                urls: list[str] = []
                thread_comments: list[ThreadComment] = []
                diff_hunk = ""

                if isinstance(comment_nodes_obj, list):
                    for c_obj in cast(list[object], comment_nodes_obj):
                        if not isinstance(c_obj, dict):
                            continue
                        c = cast(dict[str, object], c_obj)

                        # Extract URL
                        u = c.get("url")
                        if isinstance(u, str):
                            urls.append(u)

                        # Extract comment details
                        body_raw = c.get("body")
                        body = body_raw if isinstance(body_raw, str) else ""

                        created_raw = c.get("createdAt")
                        created_at = created_raw if isinstance(created_raw, str) else ""

                        author_obj = c.get("author")
                        author = ""
                        if isinstance(author_obj, dict):
                            login_raw = cast(dict[str, object], author_obj).get("login")
                            author = login_raw if isinstance(login_raw, str) else ""

                        # Get diff_hunk from first comment (they share the same context)
                        if not diff_hunk:
                            hunk_raw = c.get("diffHunk")
                            if isinstance(hunk_raw, str):
                                # Extract last line of diff hunk for context
                                lines = hunk_raw.strip().split("\n")
                                diff_hunk = lines[-1] if lines else ""

                        thread_comments.append(
                            {
                                "url": u if isinstance(u, str) else "",
                                "author": author,
                                "body": body,
                                "created_at": created_at,
                            }
                        )

                threads_out.append(
                    {
                        "thread_id": tid,
                        "is_resolved": is_resolved,
                        "path": path,
                        "line": line,
                        "diff_hunk": diff_hunk,
                        "comments": thread_comments,
                        "comment_urls": urls,
                    }
                )
                if len(threads_out) >= limit:
                    return {"ok": True, "repo": repo, "pr_number": pr_number, "threads": threads_out[:limit]}

        page_info = cast(dict[str, object], page_info_obj) if isinstance(page_info_obj, dict) else None
        if page_info is None or page_info.get("hasNextPage") is not True:
            break
        after_raw = page_info.get("endCursor")
        after = after_raw if isinstance(after_raw, str) else None

    return {"ok": True, "repo": repo, "pr_number": pr_number, "threads": threads_out[:limit]}
