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

package forgejo

import (
	"context"
	"fmt"

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

type repository struct {
	common

	name string
	h    helpers_repository.Interface
	f    *f3.Repository
}

func (o *repository) SetNative(repository any) {
	o.name = repository.(string)
}

func (o *repository) GetNativeID() string {
	return o.name
}

func (o *repository) NewFormat() f3.Interface {
	return &f3.Repository{}
}

func (o *repository) ToFormat() f3.Interface {
	return &f3.Repository{
		Common:    f3.NewCommon(o.GetNativeID()),
		Name:      o.GetNativeID(),
		FetchFunc: o.f.FetchFunc,
	}
}

func (o *repository) FromFormat(content f3.Interface) {
	f := content.Clone().(*f3.Repository)
	o.f = f
	o.f.SetID(f.Name)
	o.name = f.Name
}

func (o *repository) Get(ctx context.Context) bool {
	return o.h.Get(ctx)
}

func (o *repository) Put(ctx context.Context) id.NodeID {
	return o.upsert(ctx)
}

func (o *repository) Patch(ctx context.Context) {
	o.upsert(ctx)
}

func (o *repository) upsert(ctx context.Context) id.NodeID {
	o.Trace("%s", o.GetNativeID())
	o.h.Upsert(ctx, o.f)
	return id.NewNodeID(o.f.Name)
}

func (o *repository) urlToRepositoryURL(url string) string {
	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	repositoryURL := fmt.Sprintf("%s/%s/%s", url, owner, project)
	if o.f.GetID() == f3.RepositoryNameWiki {
		repositoryURL += ".wiki"
	}
	return repositoryURL
}

func (o *repository) GetHelper() any {
	return o.h
}

func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination, internalRef string)) {
	o.f.FetchFunc = fetchFunc
}

func (o *repository) GetRepositoryURL() string {
	return o.urlToRepositoryURL(o.getPushURL())
}

func (o *repository) GetRepositoryInternalRef() string {
	return "refs/pull/*"
}

func (o *repository) GetPullRequestBranch(prBranch *f3.PullRequestBranch) *f3.PullRequestBranch {
	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	branch, resp, err := o.getClient().GetRepoBranch(owner, project, prBranch.Ref)
	if resp.StatusCode == 404 {
		return nil
	}
	if err != nil {
		panic(fmt.Errorf("GetRepoBranch: %v %w", prBranch, err))
	}
	return &f3.PullRequestBranch{
		Ref: branch.Name,
		SHA: branch.Commit.ID,
	}
}

func (o *repository) CreatePullRequestBranch(prBranch *f3.PullRequestBranch) {
	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	_, _, err := o.getClient().CreateBranch(owner, project, forgejo_sdk.CreateBranchOption{
		BranchName: prBranch.Ref,
		OldRefName: prBranch.SHA,
	})
	if err != nil {
		panic(fmt.Errorf("CreateRepoBranch: %v %w", prBranch, err))
	}
}

func (o *repository) DeletePullRequestBranch(prBranch *f3.PullRequestBranch) {
	owner := f3_tree.GetOwnerName(o.GetNode())
	project := f3_tree.GetProjectName(o.GetNode())
	_, _, err := o.getClient().DeleteRepoBranch(owner, project, prBranch.Ref)
	if err != nil {
		panic(fmt.Errorf("CreateRepoBranch: %v %w", prBranch, err))
	}
}

func newRepository(ctx context.Context) generic.NodeDriverInterface {
	r := &repository{
		f: &f3.Repository{},
	}
	r.h = helpers_repository.NewHelper(r)
	return r
}
