package repostatus

import (
	"strings"
	"unicode/utf8"

	"io/ioutil"

	"go.wit.com/log"
)

func (rs *RepoStatus) String() string {
	return rs.path.String()
}

/*
func (rs *RepoStatus) GetPath() string {
	return rs.path.String()
}
*/

func (rs *RepoStatus) GetCurrentBranchName() string {
	return rs.currentBranch.String()
}

func (rs *RepoStatus) GetCurrentBranchVersion() string {
	return rs.currentVersion.String()
}

func (rs *RepoStatus) GetLastTagVersion() string {
	return rs.lasttag.String()
}

func (rs *RepoStatus) getCurrentBranchName() string {
	out := run(rs.realPath.String(), "git", "branch --show-current")
	log.Log(INFO, "getCurrentBranchName() =", out)
	rs.currentBranch.SetValue(out)
	return out
}

func (rs *RepoStatus) getCurrentBranchVersion() string {
	out := run(rs.realPath.String(), "git", "describe --tags")
	log.Log(INFO, "getCurrentBranchVersion()", out)
	rs.currentVersion.SetValue(out)
	return out
}

func (rs *RepoStatus) getLastTagVersion() string {
	out := run(rs.realPath.String(), "git", "rev-list --tags --max-count=1")
	log.Log(INFO, "getLastTagVersion()", out)
	// rs.lasttagrev = out

	lastreal := "describe --tags " + out
	// out = run(r.path, "git", "describe --tags c871d5ecf051a7dc4e3a77157cdbc0a457eb9ae1")
	out = run(rs.realPath.String(), "git", lastreal)
	rs.lasttag.SetValue(out)
	rs.tagsDrop.SetText(out)
	// rs.lastLabel.SetText(out)
	return out
}

func (rs *RepoStatus) populateTags() {
	tmp := rs.realPath.String() + "/.git/refs/tags"
	log.Log(INFO, "populateTags() path =", tmp)
	for _, tag := range listFiles(tmp) {
		if rs.tags[tag] == "" {
			log.Log(INFO, "populateTags() Adding new tag", tag)
			rs.tagsDrop.AddText(tag)
			rs.tags[tag] = "origin"
		}
	}
	// rs.tagsDrop.SetText(rs.lasttagrev)
}

func (rs *RepoStatus) getBranches() []string {
	var all []string
	var heads []string
	var remotes []string
	heads = listFiles(rs.realPath.String() + "/.git/refs/heads")
	remotes = listFiles(rs.realPath.String() + "/.git/refs/remotes")

	all = heads

	all = append(all, remotes...)

	for _, branch := range all {
		log.Log(INFO, "getBranches()", branch)
	}
	return all
}

func (rs *RepoStatus) CheckDirty() bool {
	cmd := []string{"git", "status"}
	path := rs.realPath.String()
	err, b, out := RunCmd(path, cmd)
	if err != nil {
		log.Warn("CheckDirty() status b =", b)
		log.Warn("CheckDirty() status cmd =", cmd)
		log.Warn("CheckDirty() status path =", path)
		log.Warn("CheckDirty() status out =", out)
		log.Warn("CheckDirty() status err =", err)
		log.Error(err, "CheckDirty() git status error")
		rs.dirtyLabel.SetValue("error")
		return true
	}

	last := out[strings.LastIndex(out, "\n")+1:]

	if last == "nothing to commit, working tree clean" {
		log.Log(INFO, "CheckDirty() b =", b, "path =", path, "out =", out)
		log.Log(INFO, "CheckDirty() no", rs.realPath.String())
		rs.dirtyLabel.SetValue("no")
		return false
	}
	// sometimes b gets exit status 1 when there isn't anything that has changed
	// run git status fixes that for some reason.
	log.Log(INFO, "CheckDirty() is normal dirty", rs.realPath.String())
	log.Log(INFO, "CheckDirty() is normal cmd =", cmd)
	log.Log(INFO, "CheckDirty() is normal b =", b)
	log.Log(INFO, "CheckDirty() is normal path =", path)
	log.Log(INFO, "CheckDirty() is normal out =", out)
	log.Log(INFO, "CheckDirty() is normal err =", err)

	rs.dirtyLabel.SetValue("dirty")
	return true

}

/*
func (rs *RepoStatus) CheckoutBranch(branch string) (string, string) {
	// run(rs.realPath.String(), "git", "checkout " + branch)

	realname := rs.getCurrentBranchName()
	realversion := rs.getCurrentBranchVersion()
	log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)
	return realname, realversion
}
*/

func (rs *RepoStatus) CheckoutMaster() bool {
	if rs.CheckDirty() {
		log.Log(INFO, rs.realPath.String(), "is dirty")
		return false
	}
	mName := rs.GetMasterBranchName()
	cmd := []string{"git", "checkout", mName}
	err, b, output := RunCmd(rs.realPath.String(), cmd)
	if err != nil {
		log.Log(INFO, err, b, output)
	}

	realname := rs.getCurrentBranchName()
	realversion := rs.getCurrentBranchVersion()
	log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)

	if realname != mName {
		log.Log(INFO, "git checkout failed", rs.realPath.String(), mName, "!=", realname)
		return false
	}
	rs.masterBranchVersion.SetValue(realversion)
	return true
}

func (rs *RepoStatus) CheckoutDevel() bool {
	devel := rs.develWorkingName.String()
	// user := rs.userWorkingName.String()

	log.Log(INFO, "checkoutBranch", devel)
	rs.checkoutBranch("devel", devel)
	// log.Log(INFO, "checkoutBranch", user)
	// rs.checkoutBranch("user", user)
	return true
}

func (rs *RepoStatus) checkoutBranch(level string, branch string) {
	if rs.CheckDirty() {
		log.Log(INFO, "checkoutBranch() checkDirty() == true for repo", rs.realPath.String(), "looking for branch:", branch)
		return
	}
	out := run(rs.realPath.String(), "git", "checkout "+branch)
	log.Log(INFO, rs.realPath.String(), "git checkout "+branch, "returned", out)

	realname := rs.getCurrentBranchName()
	realversion := rs.getCurrentBranchVersion()
	log.Log(INFO, rs.realPath.String(), "realname =", realname, "realversion =", realversion)

	switch level {
	case "master":
		rs.masterBranchVersion.SetValue(realversion)
	case "devel":
		rs.develBranchVersion.SetValue(realversion)
	case "user":
		rs.userBranchVersion.SetValue(realversion)
	default:
	}
}

func (rs *RepoStatus) SetMainWorkingName(s string) {
	if rs == nil {
		log.Info("rs == nil", s)
		return
	}
	if rs.gitConfig == nil {
		log.Info("rs.gitConfig == nil", s)
		rs.mainWorkingName.SetValue(s)
		return
	}
	if rs.gitConfig.branches == nil {
		log.Info("rs.gitConfig.branches == nil", s)
		rs.mainWorkingName.SetValue(s)
		return
	}
	_, ok := rs.gitConfig.branches[s]
	if ok {
		log.Info("git branch", s, "seems to exist")
		rs.mainWorkingName.SetValue(s)
		return
	}
	s = "master"
	_, ok = rs.gitConfig.branches[s]
	if ok {
		log.Info("git branch", s, "seems to exist")
		rs.mainWorkingName.SetValue(s)
		return
	}

	s = "main"
	_, ok = rs.gitConfig.branches[s]
	if ok {
		log.Info("git branch", s, "seems to exist")
		rs.mainWorkingName.SetValue(s)
		return
	}

	s = "notsure"
	log.Warn("git branch", s, "does not seem to exist. TODO: figure out the server default")
	for name, _ := range rs.gitConfig.branches {
		log.Warn("git branch found. use this?", name)
	}
	rs.mainWorkingName.SetValue(s)
}

func (rs *RepoStatus) SetDevelWorkingName(s string) {
	rs.develWorkingName.SetValue(s)
	rs.develBranchVersion.SetLabel(s)
}

func (rs *RepoStatus) SetUserWorkingName(s string) {
	rs.userWorkingName.SetValue(s)
	rs.userBranchVersion.SetLabel(s)
	// rs.userDrop.SetText(s)
}

// returns "master", "devel", os.Username, etc
func (rs *RepoStatus) GetMasterBranchName() string {
	name := rs.mainWorkingName.String()
	return name
}
func (rs *RepoStatus) GetDevelBranchName() string {
	name := rs.develWorkingName.String()
	return name
}

func (rs *RepoStatus) GetUserBranchName() string {
	name := rs.userWorkingName.String()
	return name
}

// returns the git versions like "1.3-2-laksdjf" or whatever
func (rs *RepoStatus) GetMasterVersion() string {
	name := rs.masterBranchVersion.String()
	return name
}
func (rs *RepoStatus) GetDevelVersion() string {
	name := rs.develBranchVersion.String()
	return name
}
func (rs *RepoStatus) GetUserVersion() string {
	name := rs.userBranchVersion.String()
	return name
}

func (rs *RepoStatus) SetMasterVersion(s string) {
	if rs.GetMasterVersion() == s {
		return
	}
	rs.changed = true
	log.Verbose("git", rs.GetMasterBranchName(), "is now version =", s)
	rs.masterBranchVersion.SetValue(s)
}

func (rs *RepoStatus) SetDevelVersion(s string) {
	if rs.GetDevelVersion() == s {
		return
	}
	rs.changed = true
	rs.develBranchVersion.SetValue(s)
	log.Verbose("git", rs.GetDevelBranchName(), "s now version =", s)
}

func (rs *RepoStatus) SetUserVersion(s string) {
	if rs.GetUserVersion() == s {
		return
	}
	rs.changed = true
	rs.userBranchVersion.SetValue(s)
	// log.Verbose("git", rs.GetUserBranchName(), "is now version =", s)
}

func (rs *RepoStatus) GetStatus() string {
	rs.changed = false
	if rs.CheckDirty() {
		log.Log(INFO, "CheckDirty() true")
		return "dirty"
	}
	if rs.userBranchVersion.String() != rs.develBranchVersion.String() {
		return "merge to devel"
	}
	if rs.develBranchVersion.String() != rs.masterBranchVersion.String() {
		return "merge to main"
	}
	if rs.lasttag.String() != rs.masterBranchVersion.String() {
		return "ready to tag version"
	}

	if rs.CheckBranches() {
		log.Log(INFO, "Branches are Perfect")
		return "PERFECT"
	}
	log.Log(INFO, rs.String(), "Branches are not Perfect")
	return "unknown branches"
}

// TODO: make this report the error somewhere
func (rs *RepoStatus) CheckBranches() bool {
	var hashCheck string
	var perfect bool = true
	all := rs.getBranches()
	path := rs.realPath.String() + "/.git/refs/"
	for _, b := range all {
		parts := strings.Split(b, "/")
		rdir := "heads"
		if len(parts) == 2 {
			rdir = "remotes"
		}
		fullfile := path + "/" + rdir + "/" + b

		// check if the ref name is "HEAD". if so, skip
		runeCount := utf8.RuneCountInString(fullfile)
		// Convert the string to a slice of runes
		runes := []rune(fullfile)
		// Slice the last 4 runes
		lastFour := runes[runeCount-4:]
		if string(lastFour) == "HEAD" {
			log.Log(INFO, "skip HEAD fullfile", fullfile)
			continue
		}

		content, _ := ioutil.ReadFile(fullfile)
		hash := strings.TrimSpace(string(content))
		if hashCheck == "" {
			hashCheck = hash
		}
		var cmd []string
		cmd = append(cmd, "git", "show", "-s", "--format=%ci", hash)
		_, _, output := RunCmd(rs.realPath.String(), cmd)
		// git show -s --format=%ci <hash> will give you the time
		// log.Log(INFO, fullfile)
		if hash == hashCheck {
			log.Log(INFO, hash, output, b)
		} else {
			log.Warn("UNKNOWN BRANCHES IN THIS REPO")
			rs.versionCmdOutput.SetText("UNKNOWN BRANCHES")
			perfect = false
			parts := strings.Split(b, "/")
			log.Warn("git push", parts)
		}
	}
	return perfect
}