package gittags

import (
	"encoding/hex"

	"github.com/blang/semver"
)

type GitOid [20]byte

func (o GitOid) String() string {
	return hex.EncodeToString(o[:])
}

type RefInfo interface {
	Name() string
	CommitOid() GitOid
}

type refInfo struct {
	name      string
	commitOid GitOid
}

var _ RefInfo = (*refInfo)(nil)

func (c *refInfo) Name() string {
	return c.name
}

func (c *refInfo) CommitOid() GitOid {
	return c.commitOid
}

type TagInfo interface {
	RefInfo
	Annotated() bool
	TagOid() (GitOid, bool)
	withCommitOid(oid GitOid) TagInfo
}

type tagInfo struct {
	name      string
	commitOid GitOid
	tagOid    *GitOid // or nil if this is not an annotated tag
}

var _ TagInfo = (*tagInfo)(nil)

func (c *tagInfo) Name() string {
	return c.name
}

func (c *tagInfo) CommitOid() GitOid {
	return c.commitOid
}

func (c *tagInfo) TagOid() (GitOid, bool) {
	if !c.Annotated() {
		var oid GitOid
		return oid, false
	}
	return *c.tagOid, true
}

func (c *tagInfo) Annotated() bool {
	return c.tagOid != nil
}

func (c *tagInfo) withCommitOid(oid GitOid) TagInfo {
	return &tagInfo{
		name:      c.name,
		commitOid: oid,
		tagOid:    &c.commitOid,
	}
}

type ReleaseTagInfo interface {
	TagInfo
	Version() semver.Version
}

type releaseTagInfo struct {
	name      string // e.g. "refs/tags/<tagName>"
	commitOid GitOid
	tagOid    *GitOid
	version   semver.Version
}

var _ ReleaseTagInfo = (*releaseTagInfo)(nil)

func (c *releaseTagInfo) Name() string {
	return c.name
}

func (c *releaseTagInfo) CommitOid() GitOid {
	return c.commitOid
}

func (c *releaseTagInfo) TagOid() (GitOid, bool) {
	if !c.Annotated() {
		var oid GitOid
		return oid, false
	}
	return *c.tagOid, true
}

func (c *releaseTagInfo) Annotated() bool {
	return c.tagOid != nil
}

func (c *releaseTagInfo) withCommitOid(oid GitOid) TagInfo {
	return &releaseTagInfo{
		name:      c.name,
		version:   c.version,
		commitOid: oid,
		tagOid:    &c.commitOid,
	}
}

func (c *releaseTagInfo) Version() semver.Version {
	return c.version
}
