package repoversion

import (
	"fmt"
	"strings"

	"github.com/go-git/go-git/v5"
	"github.com/go-git/go-git/v5/plumbing"
	"github.com/go-git/go-git/v5/plumbing/object"
)

type Version struct {
	Tag  string
	Hash string
}

func Determine(path string) (v Version, err error) {
	repo, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{DetectDotGit: true})
	if err != nil {
		return Version{}, err
	}

	tags, err := repo.Tags()
	if err != nil {
		return Version{}, err
	}

	head, err := repo.Head()
	if err != nil {
		return Version{}, err
	}

	cIter, err := repo.Log(&git.LogOptions{Order: git.LogOrderCommitterTime})
	if err != nil {
		return Version{}, err
	}

	var (
		tag             string
		tagHash         string
		commitIndex     int
		taggerTimestamp int64
	)

	idMap := make(map[string]int)

	err = cIter.ForEach(func(c *object.Commit) error {
		idMap[c.Hash.String()] = commitIndex
		commitIndex++

		return nil
	})
	if err != nil {
		return Version{}, err
	}

	err = tags.ForEach(func(t *plumbing.Reference) error {
		obj, err := repo.TagObject(t.Hash())

		if err == nil {

			_, exists := idMap[obj.Target.String()]

			if !exists {
				return nil
			}

			if taggerTimestamp < obj.Tagger.When.Unix() {
				taggerTimestamp = obj.Tagger.When.Unix()

				tag = strings.TrimPrefix(obj.Name, "v")
				tagHash = obj.Target.String()
			}
		} else {
			_, exists := idMap[t.Hash().String()]

			if !exists {
				return nil
			}

			commit, err := repo.CommitObject(t.Hash())
			if err != nil {
				return err
			}

			if taggerTimestamp < commit.Committer.When.Unix() {
				taggerTimestamp = commit.Committer.When.Unix()

				tag = strings.TrimPrefix(t.Name().Short(), "v")
				tagHash = t.Hash().String()
			}
		}

		return nil
	})
	if err != nil {
		return Version{}, err
	}

	const subHashLen int = 8

	tagHashIndex := idMap[tagHash]
	headHashText := head.Hash().String()
	subHeadHash := headHashText

	if len(headHashText) > subHashLen {
		subHeadHash = subHeadHash[:subHashLen]
	}

	v.Tag = tag
	v.Hash = headHashText

	if tagHashIndex > 0 {
		v.Tag = fmt.Sprintf("%s-%s", tag, subHeadHash)
	}

	return v, nil
}
