package repostatus

import (
	"strconv"
	"strings"

	"go.wit.com/lib/gadgets"
	"go.wit.com/log"
	"go.wit.com/widget"
)

// creates the actual widgets.
// it's assumed you are always passing in a box
func (rs *RepoStatus) draw() {
	if !rs.Ready() {
		return
	}

	// display the status of the git repository
	rs.drawGitStatus()

	// display the git branches and options
	rs.drawGitBranches()

	// show standard git commit and merge controls
	rs.drawGitCommands()
}

func (rs *RepoStatus) drawGitBranches() {
	rs.gitBranchesGroup = rs.window.Box().NewGroup("branches")
	newgrid := rs.gitBranchesGroup.NewGrid("gridnuts", 2, 2)

	rs.lasttag = gadgets.NewOneLiner(newgrid, "last tag")

	rs.masterBranchVersion = gadgets.NewOneLiner(newgrid, "master")
	rs.develBranchVersion = gadgets.NewOneLiner(newgrid, "devel")
	rs.userBranchVersion = gadgets.NewOneLiner(newgrid, "user")

	rs.currentBranch = gadgets.NewOneLiner(newgrid, "current branch")
	rs.currentVersion = gadgets.NewOneLiner(newgrid, "current version")

	rs.showBranchesButton = newgrid.NewButton("getBranches()", func() {
		all := rs.getBranches()
		i := len(all)
		log.Log(WARN, "branch count =", i)
	})

	rs.checkBranchesButton = newgrid.NewButton("CheckBranches()", func() {
		if rs.CheckBranches() {
			log.Log(WARN, "Branches are perfect")
		} else {
			log.Log(WARN, "Branches are not perfect")
		}
	})

	newgrid.NewButton("show .git/config", func() {
		if rs.gitConfig == nil {
			log.Log(WARN, "Nonexistant or damaged .git/config", rs.String())
			return
		}
		log.Log(WARN, ".git/config:", rs.realPath.String())

		// The info:
		for name, remote := range rs.gitConfig.remotes {
			log.Log(WARN, "  ", name, "url:", remote.url)
		}
		for name, branch := range rs.gitConfig.branches {
			log.Log(WARN, "  ", name, "remote:", branch.remote, "merge", branch.merge)
		}
	})

	newgrid.NewButton("CheckDirty()", func() {
		if rs.CheckDirty() {
			log.Log(WARN, "is dirty")
		} else {
			log.Log(WARN, "is not dirty")
		}
	})

	rs.TagsW = rs.TagWindow()

	newgrid.NewButton("Show tags", func() {
		if rs.TagsW == nil {
			log.Warn("error: found rs.TagsW == nil")
			rs.TagsW = rs.TagWindow()
			return
		}
		if rs.TagsW.Hidden() {
			rs.TagsW.Show()
		} else {
			rs.TagsW.Show()
		}
	})
}

func (rs *RepoStatus) drawGitStatus() {
	rs.gitStatusGroup = rs.window.Box().NewGroup("What GO Knows It Has")
	newgrid := rs.gitStatusGroup.NewGrid("gridnuts", 2, 2)
	newgrid.Margin()
	newgrid.Pad()

	rs.path = gadgets.NewOneLiner(newgrid, "path")
	rs.goSrcPath = gadgets.NewOneLiner(newgrid, "~/go/src")
	rs.realPath = gadgets.NewOneLiner(newgrid, "full path")
	rs.realPath.Hide()
	rs.mainWorkingName = gadgets.NewOneLiner(newgrid, "main working branch")
	rs.mainWorkingName.SetValue("???")
	rs.develWorkingName = gadgets.NewOneLiner(newgrid, "devel working branch")
	rs.develWorkingName.SetValue("devel")
	rs.userWorkingName = gadgets.NewOneLiner(newgrid, "user working branch")
	rs.userWorkingName.SetValue("uid")
	rs.tagsDrop = gadgets.NewBasicDropdown(newgrid, "all releases")

	// git for-each-ref --sort=taggerdate --format '%(tag) ,,,_,,, %(subject)' refs/tags
	var cmd []string
	cmd = append(cmd, "git", "for-each-ref", "--sort=taggerdate", "--format", "%(tag) %(subject)", "refs/tags")
	_, _, output := RunCmd(rs.realPath.String(), cmd)
	log.Log(INFO, output)
	for _, line := range strings.Split(output, "\n") {
		rs.tagsDrop.AddText(line)
	}

	rs.dirtyLabel = gadgets.NewOneLiner(newgrid, "dirty")
	rs.readOnly = gadgets.NewOneLiner(newgrid, "read only")
	rs.goSumStatus = gadgets.NewOneLiner(newgrid, "go mod status")
	rs.targetReleaseVersion = gadgets.NewOneLiner(newgrid, "target release version")

	rs.speed = gadgets.NewOneLiner(newgrid, "refresh speed =")
	rs.speedActual = gadgets.NewOneLiner(newgrid, "speed actual =")
}

func (rs *RepoStatus) SetGoSumStatus(s string) {
	rs.goSumStatus.SetText(s)
}

func (rs *RepoStatus) RunDevelMergeB() bool {
	master := rs.mainWorkingName.String()
	log.Warn("RunDevelMergeB() checking out master branch", master)
	rs.checkoutBranch("master", master)
	if rs.getCurrentBranchName() != master {
		log.Warn("RunDevelMergeB() something went wrong switching to the master branch. full stop!")
		return false
	}
	log.Warn("RunDevelMergeB() running runGitCommands()")
	if !rs.runGitCommands(true) {
		log.Warn("RunDevelMergeB() SOMETHING WENT WRONG")
		return false
	}
	rs.Update()
	log.Warn("RunDevelMergeB() THINGS SEEM OK runGitCommands() returned true.")
	return true
}

func (rs *RepoStatus) runReleaseVersionB() bool {
	if !rs.generateCmd() {
		log.Warn("something is wrong. fix the errors first")
		return false
	}
	rs.releaseVersion.Disable()
	log.Warn("MAKING A RELEASE AND VERSION")
	if !rs.runGitCommands(true) {
		log.Warn("SOMETHING WENT WRONG")
		rs.Update()
		rs.Enable()
		return false
	}
	log.Warn("THINGS SEEM OK. runReleaseVersionB worked. Release is finished. restart autotypist()")
	rs.Hide()
	return true
}

func (rs *RepoStatus) runFullAutomation() bool {
	if !rs.RunDevelMergeB() {
		log.Warn("THINGS FAILED returned false")
		return false
	}
	log.Warn("THINGS SEEM OK returned true. can run this again?")
	log.Warn("develMerge =", rs.develMergeB.String())
	label := "merge devel into " + rs.GetMasterBranchName()
	if label == rs.develMergeB.String() {
		log.Warn("LABELS MATCH", label, rs.develMergeB.String())
		if !rs.RunDevelMergeB() {
			log.Warn("THINGS FAILED returned false")
			return false
		}
	}
	if rs.develMergeB.String() == "ready to release" {
		log.Warn("THINGS SEEM OK ready to release")
		if rs.releaseVersion.String() == "Release!" {
			log.Warn("releaseVersion == 'Release!'. safe to do release version HERE?")
			log.Warn("rs.newversion =", rs.newversion.String())
			log.Warn("rs.targetReleaseVersion =", rs.targetReleaseVersion.String())
			log.Warn("Are these equal?")
			if rs.newversion.String() == rs.targetReleaseVersion.String() {
				log.Warn("THEY ARE EQUAL!")
				rs.runReleaseVersionB()
			} else {
				log.Warn("THEY ARE NOT EQUAL")
			}
		}
	}
	return true
}

func (rs *RepoStatus) drawGitCommands() {
	rs.gitCommandsGroup = rs.window.Box().NewGroup("git commands")
	newgrid := rs.gitCommandsGroup.NewGrid("gridnuts", 2, 2)

	newgrid.NewButton("update", func() {
		rs.Update()
	})

	newgrid.NewButton("git pull", func() {
		var cmd []string
		cmd = append(cmd, "git", "pull")
		err, b, output := RunCmd(rs.realPath.String(), cmd)
		log.Warn("Did git pull here", err, b, output)
	})

	label := "merge " + rs.userWorkingName.String() + " to " + rs.develWorkingName.String()
	rs.develMergeB = newgrid.NewButton(label, func() {
		rs.Disable()
		if rs.RunDevelMergeB() {
			log.Warn("THINGS SEEM OK fullAutomation() returned true.")
		} else {
			log.Warn("THINGS FAILED fullAutomation() returned false")
		}
		rs.Enable()
	})

	label = "merge " + rs.develWorkingName.String() + " to " + rs.mainWorkingName.String()
	rs.mainMergeB = newgrid.NewButton(label, func() {
		rs.Disable()
		if rs.RunDevelMergeB() {
			log.Warn("THINGS SEEM OK fullAutomation() returned true.")
		} else {
			log.Warn("THINGS FAILED fullAutomation() returned false")
		}
		rs.Enable()
	})

	rs.major = gadgets.NewBasicCombobox(newgrid, "major")
	rs.major.Custom = func() {
		rs.setTag()
		rs.generateCmd()
	}
	rs.major.Hide()
	rs.minor = gadgets.NewBasicCombobox(newgrid, "minor")
	rs.minor.Custom = func() {
		rs.setTag()
		rs.generateCmd()
	}
	rs.minor.Hide()
	rs.revision = gadgets.NewBasicCombobox(newgrid, "revision")
	rs.revision.Custom = func() {
		rs.setTag()
		rs.generateCmd()
	}
	rs.revision.Hide()

	// newgrid.NewLabel("new tag version")
	rs.newversion = newgrid.NewLabel("0.0.1")
	rs.newversion.Hide()

	rs.versionMessage = gadgets.NewBasicEntry(newgrid, "tag message")
	rs.versionMessage.Custom = func() {
		rs.generateCmd()
	}
	rs.versionMessage.Hide()

	rs.versionCmdOutput = gadgets.NewOneLiner(newgrid, "tag cmd")
	rs.versionCmdOutput.Hide()

	rs.releaseVersion = newgrid.NewButton("tag and release new version", func() {
		rs.Disable()
		rs.runReleaseVersionB()
	})
	rs.releaseVersion.Hide()

	newgrid.Margin()
	newgrid.Pad()
}

var releaseRevision string = ""

// over ride the computation of this if a release is being created
func (rs *RepoStatus) setRevision(c string) {
	if releaseRevision == "" {
		rs.revision.SetText(c)
	}
	rs.revision.SetText(releaseRevision)
}

func (rs *RepoStatus) SetVersion(a, b, c string, reason string) {
	rs.major.SetText(a)
	rs.minor.SetText(b)
	releaseRevision = c
	rs.setRevision(c)

	rs.targetReleaseVersion.SetText(a + "." + b + "." + c)

	rs.versionMessage.SetLabel(reason)
	rs.versionMessage.SetText(reason)
	// rs.versionMessage.SetValue(reason)
}

func (rs *RepoStatus) setTag() bool {
	lasttag := rs.lasttag.String()
	var major, minor, revision string
	major, minor, revision = splitVersion(lasttag)

	olda, _ := strconv.Atoi(major)
	oldb, _ := strconv.Atoi(minor)
	oldc, _ := strconv.Atoi(revision)

	log.Log(INFO, "current version here", lasttag)
	log.Log(INFO, "current release a,b,c =", major, minor, revision)

	newa, _ := strconv.Atoi(rs.major.String())

	newver := strconv.Itoa(newa)
	if newa < olda {
		log.Warn("new version bad", newver, "vs old version", lasttag, "newa =", newa, "olda =", olda)
		rs.newversion.SetLabel("bad")
		return false
	}
	if newa > olda {
		log.Log(INFO, "new version ok", newver, "vs old version", lasttag)
		rs.minor.SetText("0")
		rs.setRevision("0")
		newver := strconv.Itoa(newa) + ".0.0"
		rs.newversion.SetLabel(newver)
		return true
	}

	newb, _ := strconv.Atoi(rs.minor.String())
	newver = strconv.Itoa(newa) + "." + strconv.Itoa(newb)
	if newb < oldb {
		log.Warn("new version bad", newver, "vs old version", lasttag, "newb =", newb, "oldb =", oldb)
		rs.newversion.SetLabel("bad")
		return false
	}

	if newb > oldb {
		log.Log(INFO, "new version ok", newver, "vs old version", lasttag)
		newver = strconv.Itoa(newa) + "." + strconv.Itoa(newb) + ".0"
		rs.newversion.SetLabel(newver)
		rs.setRevision("0")
		return true
	}

	newc, _ := strconv.Atoi(rs.revision.String())
	newver = strconv.Itoa(newa) + "." + strconv.Itoa(newb) + "." + strconv.Itoa(newc)
	if newc <= oldc {
		log.Warn("new version bad", newver, "vs old version", lasttag)
		rs.newversion.SetLabel("bad")
		return false
	}
	log.Log(INFO, "new version ok", newver, "vs old version", lasttag)
	rs.newversion.SetLabel(newver)
	return true
}

func (rs *RepoStatus) incrementVersion() {
	lasttag := rs.lasttag.String()
	var major, minor, revision string
	if rs.targetReleaseVersion.String() == "" {
		major, minor, revision = splitVersion(lasttag)
		log.Warn("Should release version here", lasttag)
		log.Log(INFO, "Should release a,b,c", major, minor, revision)
	} else {
		// this means there is a specific release version trying to be done
		// use the target release version instead
		major, minor, revision = splitVersion(rs.targetReleaseVersion.String())
		log.Warn("Should release version here", lasttag)
		log.Log(INFO, "Should release a,b,c", major, minor, revision)
	}

	a, _ := strconv.Atoi(major)
	b, _ := strconv.Atoi(minor)
	c, _ := strconv.Atoi(revision)

	rs.major.AddText(widget.GetString(a))
	rs.major.AddText(widget.GetString(a + 1))
	rs.major.SetText(widget.GetString(a))

	rs.minor.AddText(widget.GetString(b))
	rs.minor.AddText(widget.GetString(b + 1))
	rs.minor.SetText(widget.GetString(b))

	// rs.c := strconv.Atoi(revision)
	rs.revision.AddText(widget.GetString(c + 1))
	rs.revision.AddText(widget.GetString(c + 2))
	rs.setRevision(widget.GetString(c + 1))
}

func (rs *RepoStatus) recommend() {
	log.Log(INFO, "Is repo dirty?", rs.dirtyLabel.String())
	log.Log(INFO, "list the known tags")
	rs.DisableEverything()
	rs.populateTags()
	log.Log(INFO, "Does devel == user?", rs.develBranchVersion.String(), rs.userBranchVersion.String())
	if rs.develBranchVersion.String() != rs.userBranchVersion.String() {
		log.Log(INFO, "devel does not equal user")
		log.Log(INFO, "merge or squash?")
		rs.EnableMergeDevel()
		rs.setMergeUserCommands()
		label := "merge user into " + rs.GetDevelBranchName()
		rs.develMergeB.SetLabel(label)
		return
	}
	log.Log(INFO, "Does master == devel? ", rs.masterBranchVersion.String(), rs.develBranchVersion.String())
	if rs.masterBranchVersion.String() != rs.develBranchVersion.String() {
		log.Log(INFO, "master does not equal devel. merge devel into master")
		rs.EnableMergeDevel()
		rs.setMergeDevelCommands()
		label := "merge devel into " + rs.GetMasterBranchName()
		rs.develMergeB.SetLabel(label)
		return
	}
	rs.getLastTagVersion()
	if rs.lasttag.String() != rs.masterBranchVersion.String() {
		log.Log(INFO, "master does not equal last tag")
		rs.incrementVersion()
		rs.EnableSelectTag()
		rs.setTag()
		return
	}
	log.Log(INFO, "Is repo pushed upstream? git.wit.org or github?")
}

func (rs *RepoStatus) generateCmd() bool {
	// the length of 24 is arbitrary, but should be short
	// they should be things like perhaps, major release names
	// or something the users might see.
	// aka: "Topsy", "Picasso", "Buzz", etc

	if !rs.setTag() {
		log.Log(INFO, "tag sucked. fix your tag version")
		rs.versionMessage.SetLabel("tag message (bad version)")
		rs.releaseVersion.Disable()
		return false
	}

	log.Log(INFO, "tag is valid!!!!")
	rs.setGitCommands()

	if rs.versionMessage.String() == "" {
		log.Log(INFO, "tag message is empty!!!!")
		rs.releaseVersion.Disable()
		return false
	}
	if len(rs.versionMessage.String()) > 24 {
		rs.versionMessage.SetLabel("tag message (too long)")
	} else {
		rs.versionMessage.SetLabel("tag message")
	}
	rs.releaseVersion.SetLabel("Release!")
	rs.releaseVersion.Enable()
	return true
}

func (rs *RepoStatus) runGitCommands(verbose bool) bool {
	for _, line := range rs.versionCmds {
		s := strings.Join(line, " ")
		if verbose {
			log.Log(WARN, "RUNNING:", s)
		}
		rs.develMergeB.SetText(s)
		err, b, output := runCmd(rs.realPath.String(), line)
		if err != nil {
			log.Warn("ABEND EXECUTION")
			log.Warn("error =", err)
			log.Warn("output =", output)
			return false
		}
		log.Log(INFO, "Returned with b =", b)
		log.Log(INFO, "output was =", output)
		log.Log(INFO, "RUN DONE")
	}
	return true
}

func (rs *RepoStatus) setGitCommands() {
	var line1, line2, line3 []string
	var all [][]string

	newTag := rs.newversion.String()
	line1 = append(line1, "git", "tag", "v"+newTag, "-m", rs.versionMessage.String())
	all = append(all, line1)
	line2 = append(line2, "git", "push", "--tags")
	all = append(all, line2)
	line3 = append(line3, "git", "push", "--prune", "--tags")
	all = append(all, line3)

	rs.versionCmds = all

	var tmp []string
	// convert to displayable to the user text
	for _, line := range all {
		s := strings.Join(line, " ")
		log.Log(INFO, "s =", s)
		tmp = append(tmp, s)
	}

	rs.versionCmdOutput.SetValue(strings.Join(tmp, "\n"))
}

func (rs *RepoStatus) setMergeDevelCommands() {
	var line1, line2, line3 []string
	var all [][]string

	master := rs.GetMasterBranchName()
	devel := rs.GetDevelBranchName()

	line1 = append(line1, "git", "checkout", master)
	all = append(all, line1)
	line2 = append(line2, "git", "merge", devel)
	all = append(all, line2)
	line3 = append(line3, "git", "push")
	all = append(all, line3)

	rs.versionCmds = all

	var tmp []string
	// convert to displayable to the user text
	for _, line := range all {
		s := strings.Join(line, " ")
		log.Log(INFO, "s =", s)
		tmp = append(tmp, s)
	}

	rs.versionCmdOutput.SetValue(strings.Join(tmp, "\n"))
}

func (rs *RepoStatus) setMergeUserCommands() {
	var line1, line2, line3 []string
	var all [][]string

	devel := rs.GetDevelBranchName()
	user := rs.userWorkingName.String()

	line1 = append(line1, "git", "checkout", devel)
	all = append(all, line1)
	line2 = append(line2, "git", "merge", user)
	all = append(all, line2)
	line3 = append(line3, "git", "push")
	all = append(all, line3)

	rs.versionCmds = all

	var tmp []string
	// convert to displayable to the user text
	for _, line := range all {
		s := strings.Join(line, " ")
		log.Log(INFO, "s =", s)
		tmp = append(tmp, s)
	}

	rs.versionCmdOutput.SetValue(strings.Join(tmp, "\n"))
}