package connectors

import (
	"fmt"
	"strings"
	"time"

	"github.com/google/go-github/v69/github"
	"github.com/mudler/LocalAGI/core/agent"
	"github.com/mudler/LocalAGI/core/types"
	"github.com/mudler/LocalAGI/pkg/config"
	"github.com/mudler/LocalAGI/pkg/xlog"

	"github.com/sashabaranov/go-openai"
)

type GithubPRs struct {
	token            string
	repository       string
	owner            string
	replyIfNoReplies bool
	agent            *agent.Agent
	pollInterval     time.Duration
	client           *github.Client
}

// NewGithubIssueWatcher creates a new GithubPRs connector
// with the given configuration
// - token: Github token
// - repository: Github repository name
// - owner: Github repository owner
// - replyIfNoReplies: If true, the bot will reply to issues with no comments
func NewGithubPRWatcher(config map[string]string) *GithubPRs {
	client := github.NewClient(nil).WithAuthToken(config["token"])
	replyIfNoReplies := false
	if config["replyIfNoReplies"] == "true" {
		replyIfNoReplies = true
	}

	interval, err := time.ParseDuration(config["pollInterval"])
	if err != nil {
		interval = 10 * time.Minute
	}

	return &GithubPRs{
		client:           client,
		token:            config["token"],
		repository:       config["repository"],
		owner:            config["owner"],
		replyIfNoReplies: replyIfNoReplies,
		pollInterval:     interval,
	}
}

func (g *GithubPRs) AgentResultCallback() func(state types.ActionState) {
	return func(state types.ActionState) {
		// Send the result to the bot
	}
}

func (g *GithubPRs) AgentReasoningCallback() func(state types.ActionCurrentState) bool {
	return func(state types.ActionCurrentState) bool {
		// Send the reasoning to the bot
		return true
	}
}

func (g *GithubPRs) Start(a *agent.Agent) {
	// Start the connector
	g.agent = a

	go func() {
		ticker := time.NewTicker(g.pollInterval)
		for {
			select {
			case <-ticker.C:
				xlog.Info("Looking into github Prs...")
				g.prService()
			case <-a.Context().Done():
				xlog.Info("GithubPRs connector is now stopping")
				return
			}
		}
	}()
}

func (g *GithubPRs) prService() {
	user, _, err := g.client.Users.Get(g.agent.Context(), "")
	if err != nil {
		fmt.Printf("\nerror: %v\n", err)
		return
	}

	issues, _, err := g.client.Issues.ListByRepo(
		g.agent.Context(),
		g.owner,
		g.repository,
		&github.IssueListByRepoOptions{})
	if err != nil {
		xlog.Info("Error listing issues", err)
	}
	for _, issue := range issues {
		// Do something if not an PR
		if !issue.IsPullRequest() {
			continue
		}
		labels := []string{}
		for _, l := range issue.Labels {
			labels = append(labels, l.GetName())
		}

		// Get user that opened the issue
		userNameLogin := issue.GetUser().Login
		userName := ""
		if userNameLogin != nil {
			userName = *userNameLogin
		}

		if userName == user.GetLogin() {
			xlog.Info("Ignoring issue opened by the bot")
			continue
		}
		messages := []openai.ChatCompletionMessage{
			{
				Role: "system",
				Content: fmt.Sprintf(
					`This is a conversation with an user ("%s") that opened a Github issue with title "%s" in the repository "%s" owned by "%s". The issue is the issue number %d. Current labels: %+v`, userName, issue.GetTitle(), g.repository, g.owner, issue.GetNumber(), labels),
			},
			{
				Role:    "user",
				Content: issue.GetBody(),
			},
		}

		comments, _, _ := g.client.Issues.ListComments(g.agent.Context(), g.owner, g.repository, issue.GetNumber(),
			&github.IssueListCommentsOptions{})

		mustAnswer := false
		botAnsweredAlready := false
		for i, comment := range comments {
			role := "user"
			if comment.GetUser().GetLogin() == user.GetLogin() {
				botAnsweredAlready = true
				role = "assistant"
			}
			messages = append(messages, openai.ChatCompletionMessage{
				Role:    role,
				Content: comment.GetBody(),
			})

			// if last comment is from the user and mentions the bot username, we must answer
			if comment.User.GetName() != user.GetLogin() && len(comments)-1 == i {
				if strings.Contains(comment.GetBody(), fmt.Sprintf("@%s", user.GetLogin())) {
					xlog.Info("Bot was mentioned in the last comment")
					mustAnswer = true
				}
			}
		}

		if len(comments) == 0 || !botAnsweredAlready {
			// if no comments, or bot didn't answer yet, we must answer
			xlog.Info("No comments, or bot didn't answer yet",
				"comments", len(comments),
				"botAnsweredAlready", botAnsweredAlready,
				"agent", g.agent.Character.Name,
			)
			mustAnswer = true
		}

		if len(comments) != 0 && g.replyIfNoReplies {
			xlog.Info("Ignoring issue with comments", "issue", issue.GetNumber(), "agent", g.agent.Character.Name)
			mustAnswer = false
		}

		if !mustAnswer {
			continue
		}

		res := g.agent.Ask(
			types.WithConversationHistory(messages),
		)
		if res.Error != nil {
			xlog.Error("Error asking", "error", res.Error, "agent", g.agent.Character.Name)
			return
		}

		_, _, err := g.client.Issues.CreateComment(
			g.agent.Context(),
			g.owner, g.repository,
			issue.GetNumber(), &github.IssueComment{
				Body: github.String(res.Response),
			},
		)
		if err != nil {
			xlog.Error("Error creating comment", "error", err, "agent", g.agent.Character.Name)
		}
	}
}

// GithubPRConfigMeta returns the metadata for GitHub PR connector configuration fields
func GithubPRConfigMeta() []config.Field {
	return []config.Field{
		{
			Name:     "token",
			Label:    "GitHub Token",
			Type:     config.FieldTypeText,
			Required: true,
		},
		{
			Name:     "repository",
			Label:    "Repository",
			Type:     config.FieldTypeText,
			Required: true,
		},
		{
			Name:     "owner",
			Label:    "Owner",
			Type:     config.FieldTypeText,
			Required: true,
		},
		{
			Name:  "replyIfNoReplies",
			Label: "Reply If No Replies",
			Type:  config.FieldTypeCheckbox,
		},
		{
			Name:         "pollInterval",
			Label:        "Poll Interval",
			Type:         config.FieldTypeText,
			DefaultValue: "10m",
			HelpText:     "How often to check for new PRs (e.g., 10m, 1h)",
		},
	}
}
