package gitpb

import (
	"path/filepath"
	"slices"
	"strings"
	"time"

	"go.wit.com/lib/gui/shell"
	"go.wit.com/log"
	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
)

// reload the tags
func (repo *Repo) reloadGitTags() error {
	// todo: look for changes in the tags?
	repo.Tags = new(GitTags)

	tags := []string{"%(objectname)", "%(creatordate)", "%(*authordate)", "%(refname)", "%(subject)"}
	format := strings.Join(tags, "_,,,_")
	cmd := []string{"git", "for-each-ref", "--sort=taggerdate", "--format", format}
	// log.Info("RUNNING:", strings.Join(cmd, " "))
	result := shell.PathRunQuiet(repo.FullPath, cmd)
	if result.Error != nil {
		log.Warn("git for-each-ref error:", result.Error)
		return result.Error
	}

	lines := result.Stdout
	// reverse the git order
	slices.Reverse(lines)

	var refname string
	var hash string
	var subject string
	var ctime *timestamppb.Timestamp
	var atime *timestamppb.Timestamp

	for i, line := range lines {
		var parts []string
		parts = make([]string, 0)
		parts = strings.Split(line, "_,,,_")
		if len(parts) != 5 {
			log.Info("tag error:", i, parts)
			continue
		}
		hash = parts[0]
		if parts[1] != "" {
			tmp := getGitDateStamp(parts[1])
			ctime = timestamppb.New(tmp)
		}
		if parts[2] != "" {
			tmp := getGitDateStamp(parts[2])
			atime = timestamppb.New(tmp)
		}
		refname = parts[3]
		subject = parts[4]

		newr := GitTag{
			Refname:     refname,
			Hash:        hash,
			Subject:     subject,
			Creatordate: ctime,
			Authordate:  atime,
		}

		repo.Tags.Append(&newr)
	}

	// also set the repo.NewestCommit
	cmd = []string{"git", "log", "-1", "--format=%cd"}
	result = shell.PathRunQuiet(repo.FullPath, cmd)
	if result.Error != nil {
		log.Warn("git for-each-ref error:", result.Error)
		return result.Error
	}
	newest := strings.Join(result.Stdout, "\n")
	newest = strings.TrimSpace(newest)
	tmp := getGitDateStamp(newest)
	repo.Times.NewestCommit = timestamppb.New(tmp)
	return nil
}

// attempt to parse "2024-12-13 15:39:57 -0600"
func parseGitDate(dateString string) time.Time {
	// now := time.Now().Format("Wed Feb 7 10:13:38 2024 -0600")
	const gitLayout = "2006-01-02 15:04:05 -0600"
	tagTime, err := time.Parse(gitLayout, dateString)
	if err != nil {
		const gitLayout2 = "2006-01-02 15:04:05 +0600"
		tagTime, err = time.Parse(gitLayout2, dateString)
	}
	if err != nil {
		log.Warn("GOT THIS IN PARSE AAA." + dateString + ".AAA")
		log.Warn(err)
		return time.Now()
	}
	return tagTime
}

// attempt to parse strict ISO 8601 format // 2025-01-07T21:22:16-06:00
func parseDateRFC3339(dateString string) time.Time {
	tagTime, err := time.Parse(time.RFC3339, dateString)
	if err != nil {
		log.Warn("GOT THIS IN PARSE AAA." + dateString + ".AAA")
		log.Warn(err)
		return time.Now()
	}
	return tagTime
}

// converts a git for-each-ref date. "Wed Feb 7 10:13:38 2024 -0600"
func getGitDateStamp(gitdefault string) time.Time {
	// now := time.Now().Format("Wed Feb 7 10:13:38 2024 -0600")
	const gitLayout = "Mon Jan 2 15:04:05 2006 -0700"
	tagTime, err := time.Parse(gitLayout, gitdefault)
	if err != nil {
		log.Warn("GOT THIS IN PARSE AAA." + gitdefault + ".AAA")
		log.Warn(err)
		return time.Now()
	}
	return tagTime
}

func (tag *GitTag) GetAge() time.Duration {
	return time.Since(tag.GetAuthordate().AsTime())
}

func (repo *Repo) NewestTag() *GitTag {
	loop := repo.Tags.SortByAge()
	for loop.Scan() {
		r := loop.Next()
		return r
	}
	return nil
}

// this should just do is.Exists(".git/refs/heads/findname")
func (repo *Repo) LocalTagExists(findname string) bool {
	fname := filepath.Join(".git/refs/heads", findname)
	if repo.Exists(fname) {
		return true
	}
	/*
		loop := repo.Tags.SortByRefname()
		for loop.Scan() {
			ref := loop.Next()
			// log.Info(repo.GoPath, ref.Refname)
			if strings.HasPrefix(ref.Refname, "refs/remotes") {
				continue
			}
			tagname := filepath.Base(ref.Refname)
			// log.Info("tag:", path, tagname, "from", repo.GoPath)
			if tagname == findname {
				// log.Info("found tag:", path, tagname, "from", repo.GoPath)
				return true
			}
		}
	*/
	return false
}

// returns true if 'taggy' is _ONLY_ a local tag
// this means you can not do a git pull or git push on it
func (repo *Repo) IsOnlyLocalTag(taggy string) bool {
	// first make sure the tag is actually even local
	if !repo.LocalTagExists(taggy) {
		// this means it's not even local now.
		return false
	}
	// okay, taggy exists, does it exist in a remote repo?
	loop := repo.Tags.SortByRefname()
	for loop.Scan() {
		ref := loop.Next()
		tagname := ref.Refname
		if strings.HasPrefix(tagname, "refs/remotes") {
			path, filename := filepath.Split(tagname)
			if filename == taggy {
				log.Log(INFO, "found tag:", path, filename, "from", repo.GetGoPath())
				return false
			}
		}
	}
	// we couldn't find the local tag anywhere remote, so it's probably only local
	return true
}