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

package repository

import (
	"context"
	"os"
	"runtime"

	"code.forgejo.org/f3/gof3/v3/f3"
	"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"
	"code.forgejo.org/f3/gof3/v3/util"
)

type Interface interface {
	f3_tree.RepositoryDriverInterface
	Upsert(context.Context, *f3.Repository) id.NodeID
	Get(context.Context) bool
	Fetch(context.Context) string
}

type repositoryInterface interface {
	logger.MessageInterface
	f3_tree.RepositoryDriverInterface
	GetNode() generic.NodeInterface
	ToFormat() f3.Interface
	SetFetchFunc(func(ctx context.Context, destination, internalRef string))
}

type helper struct {
	r   repositoryInterface
	dir *string
}

func (o *helper) getDir() string {
	if o.dir == nil {
		dir, err := os.MkdirTemp("", "repositoryHelper")
		if err != nil {
			panic(err)
		}
		runtime.SetFinalizer(o, func(o *helper) {
			err := os.RemoveAll(dir)
			if err != nil {
				panic(err)
			}
		})
		o.dir = &dir
	}
	return *o.dir
}

func (o *helper) GetRepositoryURL() string         { return o.r.GetRepositoryURL() }
func (o *helper) GetRepositoryInternalRef() string { return o.r.GetRepositoryInternalRef() }
func (o *helper) GetPullRequestBranch(pr *f3.PullRequestBranch) *f3.PullRequestBranch {
	return o.r.GetPullRequestBranch(pr)
}
func (o *helper) CreatePullRequestBranch(pr *f3.PullRequestBranch) { o.r.CreatePullRequestBranch(pr) }
func (o *helper) DeletePullRequestBranch(pr *f3.PullRequestBranch) { o.r.DeletePullRequestBranch(pr) }

func (o *helper) Fetch(ctx context.Context) string {
	to := o.getDir()
	o.r.Trace("%s", to)
	if err := util.CommandWithErr(ctx, util.CommandOptions{}, "git", "-C", to, "rev-parse", "--is-bare-repository"); err != nil {
		o.r.Trace(util.Command(ctx, o.r, "git", "-C", to, "init", "--bare"))
	}
	from := o.r.GetRepositoryURL()
	GitMirror(ctx, o.r, from, to, "")
	return to
}

func (o *helper) Get(ctx context.Context) bool {
	from := o.r.GetRepositoryURL()
	o.r.Trace("%s", from)
	o.r.SetFetchFunc(func(ctx context.Context, destination, internalRef string) {
		o.r.Trace("git clone %s %s", from, destination)
		GitMirror(ctx, o.r, from, destination, internalRef)
	})
	return true
}

func (o *helper) Upsert(ctx context.Context, f *f3.Repository) id.NodeID {
	if f.FetchFunc != nil {
		to := o.r.GetRepositoryURL()
		internalRef := o.r.GetRepositoryInternalRef()
		o.r.Trace("%s", to)
		f.FetchFunc(ctx, to, internalRef)
	} else {
		o.r.Trace("NO FETCH %s", o.r.GetRepositoryURL())
		panic("")
	}
	return o.r.GetNode().GetID()
}

func NewHelper(r repositoryInterface) Interface {
	h := &helper{r: r}
	return h
}
