package forge

import (
	"time"

	forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"

	"github.com/host-uk/core/pkg/log"
)

// PRMeta holds structural signals from a pull request,
// used by the pipeline MetaReader for AI-driven workflows.
type PRMeta struct {
	Number       int64
	Title        string
	State        string
	Author       string
	Branch       string
	BaseBranch   string
	Labels       []string
	Assignees    []string
	IsMerged     bool
	CreatedAt    time.Time
	UpdatedAt    time.Time
	CommentCount int
}

// Comment represents a comment with metadata.
type Comment struct {
	ID        int64
	Author    string
	Body      string
	CreatedAt time.Time
	UpdatedAt time.Time
}

const commentPageSize = 50

// GetPRMeta returns structural signals for a pull request.
// This is the Forgejo side of the dual MetaReader described in the pipeline design.
func (c *Client) GetPRMeta(owner, repo string, pr int64) (*PRMeta, error) {
	pull, _, err := c.api.GetPullRequest(owner, repo, pr)
	if err != nil {
		return nil, log.E("forge.GetPRMeta", "failed to get PR metadata", err)
	}

	meta := &PRMeta{
		Number:     pull.Index,
		Title:      pull.Title,
		State:      string(pull.State),
		Branch:     pull.Head.Ref,
		BaseBranch: pull.Base.Ref,
		IsMerged:   pull.HasMerged,
	}

	if pull.Created != nil {
		meta.CreatedAt = *pull.Created
	}
	if pull.Updated != nil {
		meta.UpdatedAt = *pull.Updated
	}

	if pull.Poster != nil {
		meta.Author = pull.Poster.UserName
	}

	for _, label := range pull.Labels {
		meta.Labels = append(meta.Labels, label.Name)
	}

	for _, assignee := range pull.Assignees {
		meta.Assignees = append(meta.Assignees, assignee.UserName)
	}

	// Fetch comment count from the issue side (PRs are issues in Forgejo).
	// Paginate to get an accurate count.
	count := 0
	page := 1
	for {
		comments, _, listErr := c.api.ListIssueComments(owner, repo, pr, forgejo.ListIssueCommentOptions{
			ListOptions: forgejo.ListOptions{Page: page, PageSize: commentPageSize},
		})
		if listErr != nil {
			break
		}
		count += len(comments)
		if len(comments) < commentPageSize {
			break
		}
		page++
	}
	meta.CommentCount = count

	return meta, nil
}

// GetCommentBodies returns all comment bodies for a pull request.
func (c *Client) GetCommentBodies(owner, repo string, pr int64) ([]Comment, error) {
	var comments []Comment
	page := 1

	for {
		raw, _, err := c.api.ListIssueComments(owner, repo, pr, forgejo.ListIssueCommentOptions{
			ListOptions: forgejo.ListOptions{Page: page, PageSize: commentPageSize},
		})
		if err != nil {
			return nil, log.E("forge.GetCommentBodies", "failed to get PR comments", err)
		}

		if len(raw) == 0 {
			break
		}

		for _, rc := range raw {
			comment := Comment{
				ID:        rc.ID,
				Body:      rc.Body,
				CreatedAt: rc.Created,
				UpdatedAt: rc.Updated,
			}
			if rc.Poster != nil {
				comment.Author = rc.Poster.UserName
			}
			comments = append(comments, comment)
		}

		if len(raw) < commentPageSize {
			break
		}
		page++
	}

	return comments, nil
}

// GetIssueBody returns the body text of an issue.
func (c *Client) GetIssueBody(owner, repo string, issue int64) (string, error) {
	iss, _, err := c.api.GetIssue(owner, repo, issue)
	if err != nil {
		return "", log.E("forge.GetIssueBody", "failed to get issue body", err)
	}

	return iss.Body, nil
}
