package cmd

import (
	"context"
	"fmt"
	"strings"

	"code.gitea.io/sdk/gitea"
	"git.jonasfranz.software/JonasFranzDEV/gitea-github-migrator/migrations"
	"github.com/google/go-github/github"
	"github.com/urfave/cli"
	"golang.org/x/oauth2"
)

// CmdMigrate migrates a given repository to gitea
var CmdMigrate = cli.Command{
	Name:   "migrate",
	Usage:  "migrates a github to a gitea repository",
	Action: runMigrate,
	Flags: append(defaultMigrateFlags,
		cli.StringFlag{
			Name:   "gh-repo",
			Usage:  "GitHub Repository",
			Value:  "username/reponame",
			EnvVar: "GH_REPOSITORY",
		},
	),
}

func runMigrate(ctx *cli.Context) error {
	m := &migrations.Migratory{
		Client:     gitea.NewClient(ctx.String("url"), ctx.String("token")),
		Private:    ctx.Bool("private"),
		NewOwnerID: ctx.Int64("owner"),
	}
	if m.NewOwnerID == 0 {
		usr, err := m.Client.GetMyUserInfo()
		if err != nil {
			return fmt.Errorf("cannot fetch user info about current user: %v", err)
		}
		m.NewOwnerID = usr.ID
	}
	c := context.Background()
	var gc *github.Client
	if ctx.IsSet("gh-token") {
		ts := oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: ctx.String("gh-token")},
		)
		tc := oauth2.NewClient(c, ts)
		gc = github.NewClient(tc)
	} else {
		gc = github.NewClient(nil)
	}

	username := strings.Split(ctx.String("gh-repo"), "/")[0]
	repo := strings.Split(ctx.String("gh-repo"), "/")[1]

	return migrate(c, gc, m, username, repo, ctx.Bool("only-repo"))
}

func migrate(c context.Context, gc *github.Client, m *migrations.Migratory, username, repo string, onlyRepo bool) error {
	fmt.Printf("Fetching repository %s/%s...\n", username, repo)
	gr, _, err := gc.Repositories.Get(c, username, repo)
	if err != nil {
		return fmt.Errorf("error while fetching repo[%s/%s]: %v", username, repo, err)
	}

	fmt.Printf("Migrating repository %s/%s...\n", username, repo)
	var mr *gitea.Repository
	if mr, err = m.Repository(gr); err != nil {
		return fmt.Errorf("error while migrating repo[%s/%s]: %v", username, repo, err)
	}
	fmt.Printf("Repository migrated to %s/%s\n", mr.Owner.UserName, mr.Name)

	if onlyRepo {
		return nil
	}

	fmt.Println("Fetching issues...")
	opt := &github.IssueListByRepoOptions{
		Sort:      "created",
		Direction: "asc",
		State:     "all",
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}
	var allIssues = make([]*github.Issue, 0)
	for {
		issues, resp, err := gc.Issues.ListByRepo(c, username, repo, opt)
		if err != nil {
			return fmt.Errorf("error while listing repos: %v", err)
		}
		allIssues = append(allIssues, issues...)
		if resp.NextPage == 0 {
			break
		}
		opt.Page = resp.NextPage
	}
	fmt.Println("Migrating issues...")
	for _, gi := range allIssues {
		fmt.Printf("Migrating #%d...\n", *gi.Number)
		issue, err := m.Issue(gi)
		if err != nil {
			return fmt.Errorf("migrating issue[id: %d]: %v", *gi.ID, err)
		}
		comments, _, err := gc.Issues.ListComments(c, username, repo, gi.GetNumber(), nil)
		if err != nil {
			return fmt.Errorf("fetching issue[id: %d] comments: %v", *gi.ID, err)
		}
		for _, gc := range comments {
			fmt.Printf("-> %d...", gc.ID)
			if _, err := m.IssueComment(issue, gc); err != nil {
				return fmt.Errorf("migrating issue comment [issue: %d, comment: %d]: %v", *gi.ID, gc.ID, err)
			}
			fmt.Print("Done!\n")
		}
		fmt.Printf("Migrated #%d...\n", *gi.Number)

	}
	return nil
}
