package cmd

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"strings"

	"code.gitea.io/sdk/gitea"
	"github.com/spf13/cobra"

	"git.mfocko.xyz/mfocko/frag-review/core"
)

var (
	prID   int64
	branch string

	patchCmd = &cobra.Command{
		Use:    "patch",
		Short:  "Patches the source files with comments from PR review.",
		PreRun: InitializeConfig,
		Run: func(cmd *cobra.Command, args []string) {
			client, err := gitea.NewClient(config.Gitea.InstanceURL, gitea.SetToken(config.Gitea.Token))
			core.ExitOnError("Couldn't create gitea client", err)

			err = GetPrID(client, &prID)
			core.ExitOnError("Couldn't get PR ID", err)

			comments := core.GetComments(&config, client, prID)
			core.ProcessComments(&config, &comments)
			PatchFiles(comments)
		},
	}
)

func init() {
	patchCmd.Flags().Int64VarP(&prID, "prID", "p", -1, "specifies ID of pull request containing changes")
	patchCmd.Flags().StringVarP(&branch, "branch", "b", "", "specifies branch where the review is present")
}

func GetPrID(client *gitea.Client, prID *int64) error {
	if *prID != -1 {
		return nil
	}

	if branch == "" {
		gitCmd := exec.Command("git", "branch", "--show-current")

		var capturedOutput bytes.Buffer
		gitCmd.Stdout = &capturedOutput

		err := gitCmd.Run()
		core.ExitOnError("Couldn't list git branches", err)

		branch = strings.TrimSpace(capturedOutput.String())
	}

	prs, _, err := client.ListRepoPullRequests(
		config.Gitea.Owner, config.Gitea.Repository, gitea.ListPullRequestsOptions{},
	)
	core.ExitOnError("Couldn't list pull requests", err)

	for _, pr := range prs {
		if pr.Head.Name == branch {
			*prID = pr.ID
			return nil
		}
	}
	return errors.New("no pull request has been found")
}

func WriteCommentsInFile(
	outputFile *os.File,
	lineNum uint64,
	comments []*gitea.PullReviewComment,
	nextComment, commentsLength int,
) int {
	for (nextComment < commentsLength) && comments[nextComment].LineNum == lineNum {
		comment := comments[nextComment].Body
		fmt.Fprint(outputFile, comment)
		fmt.Printf("L%04d:\n%s\n", comments[nextComment].LineNum, comment)
		nextComment++
	}
	return nextComment
}

func ProcessFile(filepath string, comments []*gitea.PullReviewComment) {
	fmt.Printf("FILE: %s\n", filepath)

	core.BackUpSource(filepath)

	inputFile, err := os.Open(filepath + ".bck")
	core.ExitOnError("Couldn't open backup file", err)
	defer inputFile.Close()

	outputFile, err := os.Create(filepath)
	core.ExitOnError("Couldn't overwrite original file", err)
	defer outputFile.Close()

	var i uint64 = 1
	nextComment, commentsLength := 0, len(comments)

	scanner := bufio.NewScanner(inputFile)
	for scanner.Scan() {
		nextComment = WriteCommentsInFile(outputFile, i, comments, nextComment, commentsLength)
		fmt.Fprintln(outputFile, scanner.Text())
		i++
	}
}

func PatchFiles(commentsByFile map[string]([]*gitea.PullReviewComment)) {
	for filepath, comments := range commentsByFile {
		ProcessFile(filepath, comments)
	}
}