package git

import (
	"context"
	"io"
	"strconv"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing/protocol/packp"
	"github.com/go-git/go-git/v5/plumbing/serverinfo"
	"github.com/go-git/go-git/v5/storage/filesystem"
)

// ReadWriteContexter is the interface required to operate on git protocols
type ReadWriteContexter interface {
	io.ReadWriteCloser
	Context() context.Context
}

type Protocoler interface {
	HTTPInfoRefs(ReadWriteContexter) error
	HTTPUploadPack(ReadWriteContexter) error
	SSHUploadPack(ReadWriteContexter) error
	SSHReceivePack(ReadWriteContexter, *Repo) error
}

// UpdateServerInfo handles updating server info for the git repo
func UpdateServerInfo(repo string) error {
	r, err := git.PlainOpen(repo)
	if err != nil {
		return err
	}
	fs := r.Storer.(*filesystem.Storage).Filesystem()
	return serverinfo.UpdateServerInfo(r.Storer, fs)
}

// HandlePushOptions handles all relevant push options for a [Repo] and saves the new [RepoMeta]
func HandlePushOptions(repo *Repo, opts []*packp.Option) error {
	var changed bool
	for _, opt := range opts {
		switch strings.ToLower(opt.Key) {
		case "desc", "description":
			changed = repo.Meta.Description != opt.Value
			repo.Meta.Description = opt.Value
		case "private":
			private, err := strconv.ParseBool(opt.Value)
			if err != nil {
				continue
			}
			changed = repo.Meta.Private != private
			repo.Meta.Private = private
		case "tags":
			tagValues := strings.Split(opt.Value, ",")
			for _, tagValue := range tagValues {
				var remove bool
				if strings.HasPrefix(tagValue, "-") {
					remove = true
					tagValue = strings.TrimPrefix(tagValue, "-")
				}
				for idx, tag := range repo.Meta.Tags {
					if strings.EqualFold(tag, tagValue) {
						if remove {
							repo.Meta.Tags = append(repo.Meta.Tags[:idx], repo.Meta.Tags[idx+1:]...)
						} else {
							repo.Meta.Tags = append(repo.Meta.Tags, strings.ToLower(tagValue))
						}
						break
					}
				}
			}
		}
	}
	if changed {
		return repo.SaveMeta()
	}
	return nil
}