package issueManager

import (
	"context"
	"fmt"
	"io"
	"strings"
	"time"

	gogithub "github.com/google/go-github/v48/github"

	"github.com/aws/eks-distro-build-tooling/tools/eksDistroBuildToolingOpsTools/pkg/github"
	"github.com/aws/eks-distro-build-tooling/tools/eksDistroBuildToolingOpsTools/pkg/logger"
	"github.com/aws/eks-distro-build-tooling/tools/eksDistroBuildToolingOpsTools/pkg/retrier"
)

type IssueManager struct {
	client      *github.Client
	sourceOwner string
	sourceRepo  string
	retrier     *retrier.Retrier
}

type Opts struct {
	SourceOwner string
	SourceRepo  string
}

func New(r *retrier.Retrier, githubClient *github.Client, opts *Opts) *IssueManager {
	return &IssueManager{
		client:      githubClient,
		sourceOwner: opts.SourceOwner,
		sourceRepo:  opts.SourceRepo,
		retrier:     r,
	}
}

type CreateIssueOpts struct {
	Title    *string
	Body     *string
	Labels   *[]string
	Assignee *string
	State    *string
}

func (p *IssueManager) CreateIssue(ctx context.Context, opts *CreateIssueOpts) (*gogithub.Issue, error) {
	i := &gogithub.IssueRequest{
		Title:    opts.Title,
		Body:     opts.Body,
		Labels:   opts.Labels,
		Assignee: opts.Assignee,
		State:    opts.State,
	}

	var issue *gogithub.Issue
	var resp *gogithub.Response
	var err error
	issue, resp, err = p.client.Issues.Create(ctx, p.sourceOwner, p.sourceRepo, i)
	if resp != nil {
		if resp.StatusCode == github.SecondaryRateLimitStatusCode {
			b, err := io.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("reading Github response body: %v", err)
			}
			if strings.Contains(string(b), github.SecondaryRateLimitResponse) {
				logger.V(4).Info("rate limited while attempting to create github issue")
				return nil, fmt.Errorf("rate limited while attempting to create github issues: %v", err)
			}
		}

		if resp.StatusCode == github.ResourceGoneStatusCode {
			b, err := io.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("reading Github response body: %v", err)
			}
			if strings.Contains(string(b), github.IssuesDisabledForRepoResponse) {
				logger.V(4).Info("Can't create an issue, issues are disabled for repo", "repo", p.sourceRepo)
				return nil, fmt.Errorf("creating Github issue: issues are disabled for repo. error: %v, response: %v", err, resp)
			}
		}
	}
	if err != nil {
		logger.V(4).Error(err, "creating Github issue", "response", resp)
		return nil, fmt.Errorf("creating Github issue: %v; resp: %v", err, resp)
	}
	logger.V(4).Info("create issue response", "response", resp.Response.StatusCode)
	logger.V(1).Info("Github issue created", "issue URL", issue.GetHTMLURL())
	return issue, nil
}

type GetIssueOpts struct {
	Owner string
	Repo  string
	Issue int
}

func (p *IssueManager) GetIssue(ctx context.Context, opts *GetIssueOpts) (*gogithub.Issue, error) {
	var issue *gogithub.Issue
	var resp *gogithub.Response
	var err error
	issue, resp, err = p.client.Issues.Get(ctx, opts.Owner, opts.Repo, opts.Issue)
	if resp != nil {
		if resp.StatusCode == github.SecondaryRateLimitStatusCode {
			b, err := io.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("reading Github response body: %v", err)
			}
			if strings.Contains(string(b), github.SecondaryRateLimitResponse) {
				logger.V(4).Info("rate limited while attempting to get github issue")
				return nil, fmt.Errorf("rate limited while attempting to get github issues: %v", err)
			}
		}

		if resp.StatusCode == github.ResourceGoneStatusCode {
			_, err := io.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("reading Github response body: %v", err)
			}
		}
	}
	if err != nil {
		logger.V(4).Error(err, "getting Github issue", "response", resp)
		return nil, fmt.Errorf("getting Github issue: %v; resp: %v", err, resp)
	}
	logger.V(4).Info("get issue response", "response", resp.Response.StatusCode)
	logger.V(1).Info("Github issue received", "issue URL", issue.GetHTMLURL())
	logger.V(4).Info("sleeping after Issue request to avoid secondary rate limiting by Github content API")
	time.Sleep(time.Second * 1)
	return issue, nil
}
