package github

import (
	"time"

	"github.com/google/go-github/v82/github"

	"github.com/github/github-mcp-server/pkg/sanitize"
)

// MinimalUser is the output type for user and organization search results.
type MinimalUser struct {
	Login      string       `json:"login"`
	ID         int64        `json:"id,omitempty"`
	ProfileURL string       `json:"profile_url,omitempty"`
	AvatarURL  string       `json:"avatar_url,omitempty"`
	Details    *UserDetails `json:"details,omitempty"` // Optional field for additional user details
}

// MinimalSearchUsersResult is the trimmed output type for user search results.
type MinimalSearchUsersResult struct {
	TotalCount        int           `json:"total_count"`
	IncompleteResults bool          `json:"incomplete_results"`
	Items             []MinimalUser `json:"items"`
}

// MinimalRepository is the trimmed output type for repository objects to reduce verbosity.
type MinimalRepository struct {
	ID            int64    `json:"id"`
	Name          string   `json:"name"`
	FullName      string   `json:"full_name"`
	Description   string   `json:"description,omitempty"`
	HTMLURL       string   `json:"html_url"`
	Language      string   `json:"language,omitempty"`
	Stars         int      `json:"stargazers_count"`
	Forks         int      `json:"forks_count"`
	OpenIssues    int      `json:"open_issues_count"`
	UpdatedAt     string   `json:"updated_at,omitempty"`
	CreatedAt     string   `json:"created_at,omitempty"`
	Topics        []string `json:"topics,omitempty"`
	Private       bool     `json:"private"`
	Fork          bool     `json:"fork"`
	Archived      bool     `json:"archived"`
	DefaultBranch string   `json:"default_branch,omitempty"`
}

// MinimalSearchRepositoriesResult is the trimmed output type for repository search results.
type MinimalSearchRepositoriesResult struct {
	TotalCount        int                 `json:"total_count"`
	IncompleteResults bool                `json:"incomplete_results"`
	Items             []MinimalRepository `json:"items"`
}

// MinimalCommitAuthor represents commit author information.
type MinimalCommitAuthor struct {
	Name  string `json:"name,omitempty"`
	Email string `json:"email,omitempty"`
	Date  string `json:"date,omitempty"`
}

// MinimalCommitInfo represents core commit information.
type MinimalCommitInfo struct {
	Message   string               `json:"message"`
	Author    *MinimalCommitAuthor `json:"author,omitempty"`
	Committer *MinimalCommitAuthor `json:"committer,omitempty"`
}

// MinimalCommitStats represents commit statistics.
type MinimalCommitStats struct {
	Additions int `json:"additions,omitempty"`
	Deletions int `json:"deletions,omitempty"`
	Total     int `json:"total,omitempty"`
}

// MinimalCommitFile represents a file changed in a commit.
type MinimalCommitFile struct {
	Filename  string `json:"filename"`
	Status    string `json:"status,omitempty"`
	Additions int    `json:"additions,omitempty"`
	Deletions int    `json:"deletions,omitempty"`
	Changes   int    `json:"changes,omitempty"`
}

// MinimalPRFile represents a file changed in a pull request.
// Compared to MinimalCommitFile, it includes the patch diff and previous filename for renames.
type MinimalPRFile struct {
	Filename         string `json:"filename"`
	Status           string `json:"status,omitempty"`
	Additions        int    `json:"additions,omitempty"`
	Deletions        int    `json:"deletions,omitempty"`
	Changes          int    `json:"changes,omitempty"`
	Patch            string `json:"patch,omitempty"`
	PreviousFilename string `json:"previous_filename,omitempty"`
}

// MinimalCommit is the trimmed output type for commit objects.
type MinimalCommit struct {
	SHA       string              `json:"sha"`
	HTMLURL   string              `json:"html_url"`
	Commit    *MinimalCommitInfo  `json:"commit,omitempty"`
	Author    *MinimalUser        `json:"author,omitempty"`
	Committer *MinimalUser        `json:"committer,omitempty"`
	Stats     *MinimalCommitStats `json:"stats,omitempty"`
	Files     []MinimalCommitFile `json:"files,omitempty"`
}

// MinimalRelease is the trimmed output type for release objects.
type MinimalRelease struct {
	ID          int64        `json:"id"`
	TagName     string       `json:"tag_name"`
	Name        string       `json:"name,omitempty"`
	Body        string       `json:"body,omitempty"`
	HTMLURL     string       `json:"html_url"`
	PublishedAt string       `json:"published_at,omitempty"`
	Prerelease  bool         `json:"prerelease"`
	Draft       bool         `json:"draft"`
	Author      *MinimalUser `json:"author,omitempty"`
}

// MinimalBranch is the trimmed output type for branch objects.
type MinimalBranch struct {
	Name      string `json:"name"`
	SHA       string `json:"sha"`
	Protected bool   `json:"protected"`
}

// MinimalTag is the trimmed output type for tag objects.
type MinimalTag struct {
	Name string `json:"name"`
	SHA  string `json:"sha"`
}

// MinimalResponse represents a minimal response for all CRUD operations.
// Success is implicit in the HTTP response status, and all other information
// can be derived from the URL or fetched separately if needed.
type MinimalResponse struct {
	ID  string `json:"id"`
	URL string `json:"url"`
}

type MinimalProject struct {
	ID               *int64            `json:"id,omitempty"`
	NodeID           *string           `json:"node_id,omitempty"`
	Owner            *MinimalUser      `json:"owner,omitempty"`
	Creator          *MinimalUser      `json:"creator,omitempty"`
	Title            *string           `json:"title,omitempty"`
	Description      *string           `json:"description,omitempty"`
	Public           *bool             `json:"public,omitempty"`
	ClosedAt         *github.Timestamp `json:"closed_at,omitempty"`
	CreatedAt        *github.Timestamp `json:"created_at,omitempty"`
	UpdatedAt        *github.Timestamp `json:"updated_at,omitempty"`
	DeletedAt        *github.Timestamp `json:"deleted_at,omitempty"`
	Number           *int              `json:"number,omitempty"`
	ShortDescription *string           `json:"short_description,omitempty"`
	DeletedBy        *MinimalUser      `json:"deleted_by,omitempty"`
	OwnerType        string            `json:"owner_type,omitempty"`
}

// MinimalReactions is the trimmed output type for reaction summaries, dropping the API URL.
type MinimalReactions struct {
	TotalCount int `json:"total_count"`
	PlusOne    int `json:"+1"`
	MinusOne   int `json:"-1"`
	Laugh      int `json:"laugh"`
	Confused   int `json:"confused"`
	Heart      int `json:"heart"`
	Hooray     int `json:"hooray"`
	Rocket     int `json:"rocket"`
	Eyes       int `json:"eyes"`
}

// MinimalIssue is the trimmed output type for issue objects to reduce verbosity.
type MinimalIssue struct {
	Number            int               `json:"number"`
	Title             string            `json:"title"`
	Body              string            `json:"body,omitempty"`
	State             string            `json:"state"`
	StateReason       string            `json:"state_reason,omitempty"`
	Draft             bool              `json:"draft,omitempty"`
	Locked            bool              `json:"locked,omitempty"`
	HTMLURL           string            `json:"html_url,omitempty"`
	User              *MinimalUser      `json:"user,omitempty"`
	AuthorAssociation string            `json:"author_association,omitempty"`
	Labels            []string          `json:"labels,omitempty"`
	Assignees         []string          `json:"assignees,omitempty"`
	Milestone         string            `json:"milestone,omitempty"`
	Comments          int               `json:"comments,omitempty"`
	Reactions         *MinimalReactions `json:"reactions,omitempty"`
	CreatedAt         string            `json:"created_at,omitempty"`
	UpdatedAt         string            `json:"updated_at,omitempty"`
	ClosedAt          string            `json:"closed_at,omitempty"`
	ClosedBy          string            `json:"closed_by,omitempty"`
	IssueType         string            `json:"issue_type,omitempty"`
}

// MinimalIssuesResponse is the trimmed output for a paginated list of issues.
type MinimalIssuesResponse struct {
	Issues     []MinimalIssue  `json:"issues"`
	TotalCount int             `json:"totalCount"`
	PageInfo   MinimalPageInfo `json:"pageInfo"`
}

// MinimalIssueComment is the trimmed output type for issue comment objects to reduce verbosity.
type MinimalIssueComment struct {
	ID                int64             `json:"id"`
	Body              string            `json:"body,omitempty"`
	HTMLURL           string            `json:"html_url"`
	User              *MinimalUser      `json:"user,omitempty"`
	AuthorAssociation string            `json:"author_association,omitempty"`
	Reactions         *MinimalReactions `json:"reactions,omitempty"`
	CreatedAt         string            `json:"created_at,omitempty"`
	UpdatedAt         string            `json:"updated_at,omitempty"`
}

// MinimalFileContentResponse is the trimmed output type for create/update/delete file responses.
type MinimalFileContentResponse struct {
	Content *MinimalFileContent `json:"content,omitempty"`
	Commit  *MinimalFileCommit  `json:"commit,omitempty"`
}

// MinimalFileContent is the trimmed content portion of a file operation response.
type MinimalFileContent struct {
	Name    string `json:"name"`
	Path    string `json:"path"`
	SHA     string `json:"sha"`
	Size    int    `json:"size,omitempty"`
	HTMLURL string `json:"html_url"`
}

// MinimalFileCommit is the trimmed commit portion of a file operation response.
type MinimalFileCommit struct {
	SHA     string               `json:"sha"`
	Message string               `json:"message,omitempty"`
	HTMLURL string               `json:"html_url,omitempty"`
	Author  *MinimalCommitAuthor `json:"author,omitempty"`
}

// MinimalPullRequest is the trimmed output type for pull request objects to reduce verbosity.
type MinimalPullRequest struct {
	Number             int              `json:"number"`
	Title              string           `json:"title"`
	Body               string           `json:"body,omitempty"`
	State              string           `json:"state"`
	Draft              bool             `json:"draft"`
	Merged             bool             `json:"merged"`
	MergeableState     string           `json:"mergeable_state,omitempty"`
	HTMLURL            string           `json:"html_url"`
	User               *MinimalUser     `json:"user,omitempty"`
	Labels             []string         `json:"labels,omitempty"`
	Assignees          []string         `json:"assignees,omitempty"`
	RequestedReviewers []string         `json:"requested_reviewers,omitempty"`
	MergedBy           string           `json:"merged_by,omitempty"`
	Head               *MinimalPRBranch `json:"head,omitempty"`
	Base               *MinimalPRBranch `json:"base,omitempty"`
	Additions          int              `json:"additions,omitempty"`
	Deletions          int              `json:"deletions,omitempty"`
	ChangedFiles       int              `json:"changed_files,omitempty"`
	Commits            int              `json:"commits,omitempty"`
	Comments           int              `json:"comments,omitempty"`
	CreatedAt          string           `json:"created_at,omitempty"`
	UpdatedAt          string           `json:"updated_at,omitempty"`
	ClosedAt           string           `json:"closed_at,omitempty"`
	MergedAt           string           `json:"merged_at,omitempty"`
	Milestone          string           `json:"milestone,omitempty"`
}

// MinimalPRBranch is the trimmed output type for pull request branch references.
type MinimalPRBranch struct {
	Ref  string               `json:"ref"`
	SHA  string               `json:"sha"`
	Repo *MinimalPRBranchRepo `json:"repo,omitempty"`
}

// MinimalPRBranchRepo is the trimmed repo info nested inside a PR branch.
type MinimalPRBranchRepo struct {
	FullName    string `json:"full_name"`
	Description string `json:"description,omitempty"`
}

type MinimalProjectStatusUpdate struct {
	ID         string       `json:"id"`
	Body       string       `json:"body,omitempty"`
	Status     string       `json:"status,omitempty"`
	CreatedAt  string       `json:"created_at,omitempty"`
	StartDate  string       `json:"start_date,omitempty"`
	TargetDate string       `json:"target_date,omitempty"`
	Creator    *MinimalUser `json:"creator,omitempty"`
}

// MinimalPullRequestReview is the trimmed output type for pull request review objects to reduce verbosity.
type MinimalPullRequestReview struct {
	ID                int64        `json:"id"`
	State             string       `json:"state"`
	Body              string       `json:"body,omitempty"`
	HTMLURL           string       `json:"html_url"`
	User              *MinimalUser `json:"user,omitempty"`
	CommitID          string       `json:"commit_id,omitempty"`
	SubmittedAt       string       `json:"submitted_at,omitempty"`
	AuthorAssociation string       `json:"author_association,omitempty"`
}

// Helper functions

func convertToMinimalPullRequestReview(review *github.PullRequestReview) MinimalPullRequestReview {
	m := MinimalPullRequestReview{
		ID:                review.GetID(),
		State:             review.GetState(),
		Body:              review.GetBody(),
		HTMLURL:           review.GetHTMLURL(),
		User:              convertToMinimalUser(review.GetUser()),
		CommitID:          review.GetCommitID(),
		AuthorAssociation: review.GetAuthorAssociation(),
	}

	if review.SubmittedAt != nil {
		m.SubmittedAt = review.SubmittedAt.Format(time.RFC3339)
	}

	return m
}

func convertToMinimalIssue(issue *github.Issue) MinimalIssue {
	m := MinimalIssue{
		Number:            issue.GetNumber(),
		Title:             issue.GetTitle(),
		Body:              issue.GetBody(),
		State:             issue.GetState(),
		StateReason:       issue.GetStateReason(),
		Draft:             issue.GetDraft(),
		Locked:            issue.GetLocked(),
		HTMLURL:           issue.GetHTMLURL(),
		User:              convertToMinimalUser(issue.GetUser()),
		AuthorAssociation: issue.GetAuthorAssociation(),
		Comments:          issue.GetComments(),
	}

	if issue.CreatedAt != nil {
		m.CreatedAt = issue.CreatedAt.Format(time.RFC3339)
	}
	if issue.UpdatedAt != nil {
		m.UpdatedAt = issue.UpdatedAt.Format(time.RFC3339)
	}
	if issue.ClosedAt != nil {
		m.ClosedAt = issue.ClosedAt.Format(time.RFC3339)
	}

	for _, label := range issue.Labels {
		if label != nil {
			m.Labels = append(m.Labels, label.GetName())
		}
	}

	for _, assignee := range issue.Assignees {
		if assignee != nil {
			m.Assignees = append(m.Assignees, assignee.GetLogin())
		}
	}

	if closedBy := issue.GetClosedBy(); closedBy != nil {
		m.ClosedBy = closedBy.GetLogin()
	}

	if milestone := issue.GetMilestone(); milestone != nil {
		m.Milestone = milestone.GetTitle()
	}

	if issueType := issue.GetType(); issueType != nil {
		m.IssueType = issueType.GetName()
	}

	if r := issue.Reactions; r != nil {
		m.Reactions = &MinimalReactions{
			TotalCount: r.GetTotalCount(),
			PlusOne:    r.GetPlusOne(),
			MinusOne:   r.GetMinusOne(),
			Laugh:      r.GetLaugh(),
			Confused:   r.GetConfused(),
			Heart:      r.GetHeart(),
			Hooray:     r.GetHooray(),
			Rocket:     r.GetRocket(),
			Eyes:       r.GetEyes(),
		}
	}

	return m
}

func fragmentToMinimalIssue(fragment IssueFragment) MinimalIssue {
	m := MinimalIssue{
		Number:    int(fragment.Number),
		Title:     sanitize.Sanitize(string(fragment.Title)),
		Body:      sanitize.Sanitize(string(fragment.Body)),
		State:     string(fragment.State),
		Comments:  int(fragment.Comments.TotalCount),
		CreatedAt: fragment.CreatedAt.Format(time.RFC3339),
		UpdatedAt: fragment.UpdatedAt.Format(time.RFC3339),
		User: &MinimalUser{
			Login: string(fragment.Author.Login),
		},
	}

	for _, label := range fragment.Labels.Nodes {
		m.Labels = append(m.Labels, string(label.Name))
	}

	return m
}

func convertToMinimalIssuesResponse(fragment IssueQueryFragment) MinimalIssuesResponse {
	minimalIssues := make([]MinimalIssue, 0, len(fragment.Nodes))
	for _, issue := range fragment.Nodes {
		minimalIssues = append(minimalIssues, fragmentToMinimalIssue(issue))
	}

	return MinimalIssuesResponse{
		Issues:     minimalIssues,
		TotalCount: fragment.TotalCount,
		PageInfo: MinimalPageInfo{
			HasNextPage:     bool(fragment.PageInfo.HasNextPage),
			HasPreviousPage: bool(fragment.PageInfo.HasPreviousPage),
			StartCursor:     string(fragment.PageInfo.StartCursor),
			EndCursor:       string(fragment.PageInfo.EndCursor),
		},
	}
}

func convertToMinimalIssueComment(comment *github.IssueComment) MinimalIssueComment {
	m := MinimalIssueComment{
		ID:                comment.GetID(),
		Body:              comment.GetBody(),
		HTMLURL:           comment.GetHTMLURL(),
		User:              convertToMinimalUser(comment.GetUser()),
		AuthorAssociation: comment.GetAuthorAssociation(),
	}

	if comment.CreatedAt != nil {
		m.CreatedAt = comment.CreatedAt.Format(time.RFC3339)
	}
	if comment.UpdatedAt != nil {
		m.UpdatedAt = comment.UpdatedAt.Format(time.RFC3339)
	}

	if r := comment.Reactions; r != nil {
		m.Reactions = &MinimalReactions{
			TotalCount: r.GetTotalCount(),
			PlusOne:    r.GetPlusOne(),
			MinusOne:   r.GetMinusOne(),
			Laugh:      r.GetLaugh(),
			Confused:   r.GetConfused(),
			Heart:      r.GetHeart(),
			Hooray:     r.GetHooray(),
			Rocket:     r.GetRocket(),
			Eyes:       r.GetEyes(),
		}
	}

	return m
}

func convertToMinimalFileContentResponse(resp *github.RepositoryContentResponse) MinimalFileContentResponse {
	m := MinimalFileContentResponse{}

	if resp == nil {
		return m
	}

	if c := resp.Content; c != nil {
		m.Content = &MinimalFileContent{
			Name:    c.GetName(),
			Path:    c.GetPath(),
			SHA:     c.GetSHA(),
			Size:    c.GetSize(),
			HTMLURL: c.GetHTMLURL(),
		}
	}

	m.Commit = &MinimalFileCommit{
		SHA:     resp.Commit.GetSHA(),
		Message: resp.Commit.GetMessage(),
		HTMLURL: resp.Commit.GetHTMLURL(),
	}

	if author := resp.Commit.Author; author != nil {
		m.Commit.Author = &MinimalCommitAuthor{
			Name:  author.GetName(),
			Email: author.GetEmail(),
		}
		if author.Date != nil {
			m.Commit.Author.Date = author.Date.Format(time.RFC3339)
		}
	}

	return m
}

func convertToMinimalPullRequest(pr *github.PullRequest) MinimalPullRequest {
	m := MinimalPullRequest{
		Number:         pr.GetNumber(),
		Title:          pr.GetTitle(),
		Body:           pr.GetBody(),
		State:          pr.GetState(),
		Draft:          pr.GetDraft(),
		Merged:         pr.GetMerged(),
		MergeableState: pr.GetMergeableState(),
		HTMLURL:        pr.GetHTMLURL(),
		User:           convertToMinimalUser(pr.GetUser()),
		Additions:      pr.GetAdditions(),
		Deletions:      pr.GetDeletions(),
		ChangedFiles:   pr.GetChangedFiles(),
		Commits:        pr.GetCommits(),
		Comments:       pr.GetComments(),
	}

	if pr.CreatedAt != nil {
		m.CreatedAt = pr.CreatedAt.Format(time.RFC3339)
	}
	if pr.UpdatedAt != nil {
		m.UpdatedAt = pr.UpdatedAt.Format(time.RFC3339)
	}
	if pr.ClosedAt != nil {
		m.ClosedAt = pr.ClosedAt.Format(time.RFC3339)
	}
	if pr.MergedAt != nil {
		m.MergedAt = pr.MergedAt.Format(time.RFC3339)
	}

	for _, label := range pr.Labels {
		if label != nil {
			m.Labels = append(m.Labels, label.GetName())
		}
	}

	for _, assignee := range pr.Assignees {
		if assignee != nil {
			m.Assignees = append(m.Assignees, assignee.GetLogin())
		}
	}

	for _, reviewer := range pr.RequestedReviewers {
		if reviewer != nil {
			m.RequestedReviewers = append(m.RequestedReviewers, reviewer.GetLogin())
		}
	}

	if mergedBy := pr.GetMergedBy(); mergedBy != nil {
		m.MergedBy = mergedBy.GetLogin()
	}

	if head := pr.Head; head != nil {
		m.Head = convertToMinimalPRBranch(head)
	}

	if base := pr.Base; base != nil {
		m.Base = convertToMinimalPRBranch(base)
	}

	if milestone := pr.GetMilestone(); milestone != nil {
		m.Milestone = milestone.GetTitle()
	}

	return m
}

func convertToMinimalPRBranch(branch *github.PullRequestBranch) *MinimalPRBranch {
	if branch == nil {
		return nil
	}

	b := &MinimalPRBranch{
		Ref: branch.GetRef(),
		SHA: branch.GetSHA(),
	}

	if repo := branch.GetRepo(); repo != nil {
		b.Repo = &MinimalPRBranchRepo{
			FullName:    repo.GetFullName(),
			Description: repo.GetDescription(),
		}
	}

	return b
}

func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject {
	if fullProject == nil {
		return nil
	}

	return &MinimalProject{
		ID:               github.Ptr(fullProject.GetID()),
		NodeID:           github.Ptr(fullProject.GetNodeID()),
		Owner:            convertToMinimalUser(fullProject.GetOwner()),
		Creator:          convertToMinimalUser(fullProject.GetCreator()),
		Title:            github.Ptr(fullProject.GetTitle()),
		Description:      github.Ptr(fullProject.GetDescription()),
		Public:           github.Ptr(fullProject.GetPublic()),
		ClosedAt:         github.Ptr(fullProject.GetClosedAt()),
		CreatedAt:        github.Ptr(fullProject.GetCreatedAt()),
		UpdatedAt:        github.Ptr(fullProject.GetUpdatedAt()),
		DeletedAt:        github.Ptr(fullProject.GetDeletedAt()),
		Number:           github.Ptr(fullProject.GetNumber()),
		ShortDescription: github.Ptr(fullProject.GetShortDescription()),
		DeletedBy:        convertToMinimalUser(fullProject.GetDeletedBy()),
	}
}

func convertToMinimalUser(user *github.User) *MinimalUser {
	if user == nil {
		return nil
	}

	return &MinimalUser{
		Login:      user.GetLogin(),
		ID:         user.GetID(),
		ProfileURL: user.GetHTMLURL(),
		AvatarURL:  user.GetAvatarURL(),
	}
}

// convertToMinimalCommit converts a GitHub API RepositoryCommit to MinimalCommit
func convertToMinimalCommit(commit *github.RepositoryCommit, includeDiffs bool) MinimalCommit {
	minimalCommit := MinimalCommit{
		SHA:     commit.GetSHA(),
		HTMLURL: commit.GetHTMLURL(),
	}

	if commit.Commit != nil {
		minimalCommit.Commit = &MinimalCommitInfo{
			Message: commit.Commit.GetMessage(),
		}

		if commit.Commit.Author != nil {
			minimalCommit.Commit.Author = &MinimalCommitAuthor{
				Name:  commit.Commit.Author.GetName(),
				Email: commit.Commit.Author.GetEmail(),
			}
			if commit.Commit.Author.Date != nil {
				minimalCommit.Commit.Author.Date = commit.Commit.Author.Date.Format(time.RFC3339)
			}
		}

		if commit.Commit.Committer != nil {
			minimalCommit.Commit.Committer = &MinimalCommitAuthor{
				Name:  commit.Commit.Committer.GetName(),
				Email: commit.Commit.Committer.GetEmail(),
			}
			if commit.Commit.Committer.Date != nil {
				minimalCommit.Commit.Committer.Date = commit.Commit.Committer.Date.Format(time.RFC3339)
			}
		}
	}

	if commit.Author != nil {
		minimalCommit.Author = &MinimalUser{
			Login:      commit.Author.GetLogin(),
			ID:         commit.Author.GetID(),
			ProfileURL: commit.Author.GetHTMLURL(),
			AvatarURL:  commit.Author.GetAvatarURL(),
		}
	}

	if commit.Committer != nil {
		minimalCommit.Committer = &MinimalUser{
			Login:      commit.Committer.GetLogin(),
			ID:         commit.Committer.GetID(),
			ProfileURL: commit.Committer.GetHTMLURL(),
			AvatarURL:  commit.Committer.GetAvatarURL(),
		}
	}

	// Only include stats and files if includeDiffs is true
	if includeDiffs {
		if commit.Stats != nil {
			minimalCommit.Stats = &MinimalCommitStats{
				Additions: commit.Stats.GetAdditions(),
				Deletions: commit.Stats.GetDeletions(),
				Total:     commit.Stats.GetTotal(),
			}
		}

		if len(commit.Files) > 0 {
			minimalCommit.Files = make([]MinimalCommitFile, 0, len(commit.Files))
			for _, file := range commit.Files {
				minimalFile := MinimalCommitFile{
					Filename:  file.GetFilename(),
					Status:    file.GetStatus(),
					Additions: file.GetAdditions(),
					Deletions: file.GetDeletions(),
					Changes:   file.GetChanges(),
				}
				minimalCommit.Files = append(minimalCommit.Files, minimalFile)
			}
		}
	}

	return minimalCommit
}

// MinimalPageInfo contains pagination cursor information.
type MinimalPageInfo struct {
	HasNextPage     bool   `json:"hasNextPage"`
	HasPreviousPage bool   `json:"hasPreviousPage"`
	StartCursor     string `json:"startCursor,omitempty"`
	EndCursor       string `json:"endCursor,omitempty"`
}

// MinimalReviewComment is the trimmed output type for PR review comment objects.
type MinimalReviewComment struct {
	Body      string `json:"body,omitempty"`
	Path      string `json:"path"`
	Line      *int   `json:"line,omitempty"`
	Author    string `json:"author,omitempty"`
	CreatedAt string `json:"created_at,omitempty"`
	UpdatedAt string `json:"updated_at,omitempty"`
	HTMLURL   string `json:"html_url"`
}

// MinimalReviewThread is the trimmed output type for PR review thread objects.
type MinimalReviewThread struct {
	IsResolved  bool                   `json:"is_resolved"`
	IsOutdated  bool                   `json:"is_outdated"`
	IsCollapsed bool                   `json:"is_collapsed"`
	Comments    []MinimalReviewComment `json:"comments"`
	TotalCount  int                    `json:"total_count"`
}

// MinimalReviewThreadsResponse is the trimmed output for a paginated list of PR review threads.
type MinimalReviewThreadsResponse struct {
	ReviewThreads []MinimalReviewThread `json:"review_threads"`
	TotalCount    int                   `json:"totalCount"`
	PageInfo      MinimalPageInfo       `json:"pageInfo"`
}

func convertToMinimalPRFiles(files []*github.CommitFile) []MinimalPRFile {
	result := make([]MinimalPRFile, 0, len(files))
	for _, f := range files {
		result = append(result, MinimalPRFile{
			Filename:         f.GetFilename(),
			Status:           f.GetStatus(),
			Additions:        f.GetAdditions(),
			Deletions:        f.GetDeletions(),
			Changes:          f.GetChanges(),
			Patch:            f.GetPatch(),
			PreviousFilename: f.GetPreviousFilename(),
		})
	}
	return result
}

// convertToMinimalBranch converts a GitHub API Branch to MinimalBranch
func convertToMinimalBranch(branch *github.Branch) MinimalBranch {
	return MinimalBranch{
		Name:      branch.GetName(),
		SHA:       branch.GetCommit().GetSHA(),
		Protected: branch.GetProtected(),
	}
}

func convertToMinimalRelease(release *github.RepositoryRelease) MinimalRelease {
	m := MinimalRelease{
		ID:         release.GetID(),
		TagName:    release.GetTagName(),
		Name:       release.GetName(),
		Body:       release.GetBody(),
		HTMLURL:    release.GetHTMLURL(),
		Prerelease: release.GetPrerelease(),
		Draft:      release.GetDraft(),
		Author:     convertToMinimalUser(release.GetAuthor()),
	}

	if release.PublishedAt != nil {
		m.PublishedAt = release.PublishedAt.Format(time.RFC3339)
	}

	return m
}

func convertToMinimalTag(tag *github.RepositoryTag) MinimalTag {
	m := MinimalTag{
		Name: tag.GetName(),
	}

	if commit := tag.GetCommit(); commit != nil {
		m.SHA = commit.GetSHA()
	}

	return m
}

// MinimalCheckRun is the trimmed output type for check run objects.
type MinimalCheckRun struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Status      string `json:"status"`
	Conclusion  string `json:"conclusion,omitempty"`
	HTMLURL     string `json:"html_url,omitempty"`
	DetailsURL  string `json:"details_url,omitempty"`
	StartedAt   string `json:"started_at,omitempty"`
	CompletedAt string `json:"completed_at,omitempty"`
}

// MinimalCheckRunsResult is the trimmed output type for check runs list results.
type MinimalCheckRunsResult struct {
	TotalCount int               `json:"total_count"`
	CheckRuns  []MinimalCheckRun `json:"check_runs"`
}

// convertToMinimalCheckRun converts a GitHub API CheckRun to MinimalCheckRun
func convertToMinimalCheckRun(checkRun *github.CheckRun) MinimalCheckRun {
	minimalCheckRun := MinimalCheckRun{
		ID:         checkRun.GetID(),
		Name:       checkRun.GetName(),
		Status:     checkRun.GetStatus(),
		Conclusion: checkRun.GetConclusion(),
		HTMLURL:    checkRun.GetHTMLURL(),
		DetailsURL: checkRun.GetDetailsURL(),
	}

	if checkRun.StartedAt != nil {
		minimalCheckRun.StartedAt = checkRun.StartedAt.Format("2006-01-02T15:04:05Z")
	}
	if checkRun.CompletedAt != nil {
		minimalCheckRun.CompletedAt = checkRun.CompletedAt.Format("2006-01-02T15:04:05Z")
	}

	return minimalCheckRun
}

func convertToMinimalReviewThreadsResponse(query reviewThreadsQuery) MinimalReviewThreadsResponse {
	threads := query.Repository.PullRequest.ReviewThreads

	minimalThreads := make([]MinimalReviewThread, 0, len(threads.Nodes))
	for _, thread := range threads.Nodes {
		minimalThreads = append(minimalThreads, convertToMinimalReviewThread(thread))
	}

	return MinimalReviewThreadsResponse{
		ReviewThreads: minimalThreads,
		TotalCount:    int(threads.TotalCount),
		PageInfo: MinimalPageInfo{
			HasNextPage:     bool(threads.PageInfo.HasNextPage),
			HasPreviousPage: bool(threads.PageInfo.HasPreviousPage),
			StartCursor:     string(threads.PageInfo.StartCursor),
			EndCursor:       string(threads.PageInfo.EndCursor),
		},
	}
}

func convertToMinimalReviewThread(thread reviewThreadNode) MinimalReviewThread {
	comments := make([]MinimalReviewComment, 0, len(thread.Comments.Nodes))
	for _, c := range thread.Comments.Nodes {
		comments = append(comments, convertToMinimalReviewComment(c))
	}

	return MinimalReviewThread{
		IsResolved:  bool(thread.IsResolved),
		IsOutdated:  bool(thread.IsOutdated),
		IsCollapsed: bool(thread.IsCollapsed),
		Comments:    comments,
		TotalCount:  int(thread.Comments.TotalCount),
	}
}

func convertToMinimalReviewComment(c reviewCommentNode) MinimalReviewComment {
	m := MinimalReviewComment{
		Body:    string(c.Body),
		Path:    string(c.Path),
		Author:  string(c.Author.Login),
		HTMLURL: c.URL.String(),
	}

	if c.Line != nil {
		line := int(*c.Line)
		m.Line = &line
	}

	if !c.CreatedAt.IsZero() {
		m.CreatedAt = c.CreatedAt.Format(time.RFC3339)
	}
	if !c.UpdatedAt.IsZero() {
		m.UpdatedAt = c.UpdatedAt.Format(time.RFC3339)
	}

	return m
}
