// Copyright Earl Warren <contact@earl-warren.org>
// Copyright Loïc Dachary <loic@dachary.org>
// SPDX-License-Identifier: MIT

package pullrequest

import (
	"context"
	"fmt"

	"code.forgejo.org/f3/gof3/v3/f3"
	helpers_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/repository"
	"code.forgejo.org/f3/gof3/v3/id"
	"code.forgejo.org/f3/gof3/v3/logger"
	f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
	"code.forgejo.org/f3/gof3/v3/tree/generic"
)

type Interface interface {
	Fetch(context.Context) id.NodeID
	Get(context.Context) bool
	EnsureBranch(ctx context.Context, prBranch *f3.PullRequestBranch) func()
}

type prInterface interface {
	logger.MessageInterface
	f3_tree.PullRequestDriverInterface
	GetNode() generic.NodeInterface
	SetFetchFunc(func(ctx context.Context, url, ref string))
	ToFormat() f3.Interface
}

type helper struct {
	pr prInterface
	r  helpers_repository.Interface
}

func (o *helper) getRepository(ctx context.Context) helpers_repository.Interface {
	if o.r == nil {
		project := f3_tree.GetFirstNodeKind(o.pr.GetNode(), f3_tree.KindProject)
		r := project.Find(generic.NewPathFromString("repositories/vcs"))
		if r == generic.NilNode {
			panic(fmt.Errorf("no repository found for %s", project.GetCurrentPath()))
		}
		o.r = r.GetDriver().(f3_tree.ForgeDriverInterface).GetHelper().(helpers_repository.Interface)
	}
	return o.r
}

func (o *helper) Get(ctx context.Context) bool {
	headURL := o.getRepository(ctx).GetRepositoryURL()
	headRef := o.pr.GetPullRequestHead(ctx)
	o.pr.SetFetchFunc(func(ctx context.Context, url, ref string) {
		helpers_repository.GitMirrorRef(ctx, o.pr, headURL, headRef, url, ref)
	})
	return true
}

func (o *helper) Fetch(ctx context.Context) id.NodeID {
	f := o.pr.ToFormat().(*f3.PullRequest)
	ref := o.pr.GetPullRequestRef()
	if f.FetchFunc != nil && ref != "" {
		url := o.getRepository(ctx).GetRepositoryURL()
		f.FetchFunc(ctx, url, ref)
	}
	return o.pr.GetNode().GetID()
}

func uniqueBranchName(prBranch *f3.PullRequestBranch) string {
	return prBranch.Ref + prBranch.SHA
}

func (o *helper) EnsureBranch(ctx context.Context, prBranch *f3.PullRequestBranch) func() {
	r := o.getRepository(ctx)
	existing := r.GetPullRequestBranch(prBranch)
	if existing != nil && existing.Equal(*prBranch) {
		return func() {}
	}
	prBranch.Ref = uniqueBranchName(prBranch)
	r.CreatePullRequestBranch(prBranch)
	return func() {
		r.DeletePullRequestBranch(prBranch)
	}
}

func NewHelper(pr prInterface) Interface {
	return &helper{
		pr: pr,
	}
}
