// This is a simple example
package main

import (
	"os"
	"path/filepath"
	"strings"

	"go.wit.com/gui"
	"go.wit.com/log"

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

var release releaseStruct

type releaseStruct struct {
	current *repo

	box      *gui.Node
	group    *gui.Node
	grid     *gui.Node
	repo     *gadgets.OneLiner
	status   *gadgets.OneLiner
	readOnly *gadgets.OneLiner
	notes    *gadgets.OneLiner
	version  *gadgets.OneLiner
	//	versionS        string
	//	widgetVersionS  string
	releaseVersionB *gui.Node
	unreleaseB      *gui.Node
	reason          *gadgets.BasicEntry
	reasonS         string

	openrepo *gui.Node

	goGetB       *gui.Node
	checkGoSumB  *gui.Node
	checkDirtyB  *gui.Node
	makeRedomodB *gui.Node
	sendVersionB *gui.Node
	checkSafeB   *gui.Node
	whitelist    map[string]*repo

	// store myself here. use myself to
	// do garbage go get tests and other potential junk
	guireleaser *repo
}

func (w *autoType) Disable() {
	// w.mainWindow.Disable()
	release.box.Disable()
	// buttonDisable()
}

func (w *autoType) Enable() {
	// w.mainWindow.Enable()
	release.box.Enable()
	// buttonEnable()
}

func createReleaseBox(box *gui.Node) {
	release.reasonS = "gocui dropdown select"

	initWhitelist()

	release.box = box
	release.group = release.box.NewGroup("things")
	release.grid = release.group.NewGrid("buildOptions", 2, 1)

	// do an initial scan of all the repos
	scanGoSum()

	release.grid.NewButton("scan for Ready", func() {
		me.Disable()
		scanForReady()
		scanForReady()
		me.Enable()
	})
	release.grid.NewButton("findNextDirty()", func() {
		me.Disable()
		defer me.Enable()
		if findNextDirty("PRIMATIVE") {
			log.Info("findNextDirty() found a repo")
			return
		}
		if findNextDirty("") {
			log.Info("findNextDirty() found a repo")
			return
		}
	})
	release.releaseVersionB = release.grid.NewButton("release version", func() {
		buttonDisable()
		if release.releaseVersionB.String() != "release version "+release.version.String() {
			log.Warn("something went wrong with the release.version:", release.version.String())
			return
		}
		if strings.HasPrefix(release.version.String(), "v") {
			log.Warn("everything is ok. version starts with v.", release.version.String())
		} else {
			log.Warn("version does not start with v.", release.version.String())
			return
		}
		switch release.status.String() {
		case "PRIMATIVE":
			log.Warn("can do PRIMATIVE", release.version.String())
		case "GOOD":
			log.Warn("GOOD. lots of go.sum checks passed", release.version.String())
		case "manually chosen":
			log.Warn("attempting manual release", release.version.String())
		case "NOT READY":
			log.Warn("attempting to release. TODO: recheck go.sum here", release.version.String())
			log.Warn("Sleep 10")
			log.Sleep(10)
		case "UNRELEASED":
			log.Warn("attempting to release. TODO: dig deep on go.sum here", release.version.String())
			log.Warn("Sleep 10")
			log.Sleep(10)
		default:
			log.Warn("what is this?", release.version.String(), release.status.String())
			return
		}

		if release.current.status.ReadOnly() {
			log.Info("sorry, it's read-only")
			return
		}
		if release.current.status.CheckDirty() {
			log.Info("sorry, it's still dirty")
			return
		}

		curName := release.current.status.GetCurrentBranchName()
		mName := release.current.status.GetMasterBranchName()
		if curName != mName {
			log.Info("\trepo is not working from main branch", curName, "!=", mName)
			return
		}

		log.Info("\ttag and push", curName, release.version.String(), release.reasonS)

		var all [][]string
		all = append(all, []string{"git", "add", "-f", "go.mod"})
		if release.status.String() == "PRIMATIVE" {
			// don't add go.sum here. TODO: check for go.sum file and fail
		} else {
			all = append(all, []string{"git", "add", "-f", "go.sum"})
		}
		all = append(all, []string{"git", "commit", "-m", release.reasonS})
		all = append(all, []string{"git", "push"})
		all = append(all, []string{"git", "tag", "-m", release.reasonS, release.version.String()})
		all = append(all, []string{"git", "push", "origin", release.version.String()})

		if doAll(release.current, all) {
			log.Info("EVERYTHING OK")

			gopath := release.current.String()
			cmd := []string{"go", "get", "-v", gopath + "@" + release.version.String()}
			log.Info("SHOULD RUN cmd HERE:", cmd)

			if release.guireleaser == nil {
				log.Info("CAN NOT SELF UPDATE HERE. cmd =", cmd)
				return
			} else {
				os.Unsetenv("GO111MODULE")
				log.Info("TRYING TO SELF UPDATE HERE. cmd =", cmd)
				err, out := release.guireleaser.status.RunCmd(cmd)
				if err == nil {
					log.Info("SELF UPDATE OK. out =", out)
					log.Info("SELF UPDATE WORKED")
					release.current.setGoSumStatus("RELEASED")

					release.current.status.UpdateCurrent()
					// repo.newScan()
					cbname := release.current.status.GetCurrentBranchName()
					cbversion := release.current.status.GetCurrentBranchVersion()
					lasttag := release.current.status.GetLastTagVersion()
					release.current.lastTag.SetLabel(lasttag)
					release.current.vLabel.SetLabel(cbname + " " + cbversion)
				} else {
					log.Info("SELF UPDATE FAILED err =", err)
					log.Info("SELF UPDATE FAILED out =", out)
					return
				}
			}
			buttonEnable()
		} else {
			log.Info("SOMETHING FAILED")
		}
	})

	release.openrepo = release.grid.NewButton("open repo", func() {
		if release.current == nil {
			log.Info("find the next repo first")
			return
		}
		// make sure read only is set
		if release.current.status.ReadOnly() {
			release.readOnly.SetValue("true")
		} else {
			release.readOnly.SetValue("false")
		}

		// do a new scan
		release.current.newScan()

		// only continue if the go.sum & go.mod files are clean
		if ok, missing := release.current.status.CheckGoSum(); ok {
			log.Info("repo has go.sum requirements that are clean")
			release.status.SetValue("CLEAN")
			release.current.setGoSumStatus("CLEAN")
		} else {
			log.Info("BAD repo has go.sum requirements that are screwed up. missing:", missing)
			release.status.SetValue("BAD")
			release.current.setGoSumStatus("BAD")
			return
		}

		if release.current.dirtyLabel.String() == "PERFECT" {
			log.Info("REPO IS STILL DIRTY")
			return
		}

		// open the status window to commit the release
		release.current.status.Toggle()
		release.current.status.UpdateCurrent()
	})
	release.openrepo.Disable()

	release.repo = gadgets.NewOneLiner(release.grid, "repo")
	release.status = gadgets.NewOneLiner(release.grid, "status")
	release.readOnly = gadgets.NewOneLiner(release.grid, "read-only")
	release.notes = gadgets.NewOneLiner(release.grid, "notes")

	release.version = gadgets.NewOneLiner(release.grid, "version")

	release.reason = gadgets.NewBasicEntry(release.grid, "release reason")
	release.reason.SetText(release.reasonS)

	me.autoWorkingPwd = gadgets.NewOneLiner(release.grid, "working directory (pwd)")
	me.userHomePwd = gadgets.NewOneLiner(release.grid, "user home")
	me.goSrcPwd = gadgets.NewOneLiner(release.grid, "go src home")

	homeDir, err := os.UserHomeDir()
	if err != nil {
		log.Warn("Error getting home directory:", err)
		homeDir = "/home/autotypist"
	}
	me.userHomePwd.SetText(homeDir)
	srcDir := filepath.Join(homeDir, "go/src")
	me.goSrcPwd.SetText(srcDir)

	testf := filepath.Join(srcDir, "go.wit.com/apps/guireleaser", "go.sum")
	if ! Exists(testf) {
		log.Info("go.sum missing", testf)
		panic("redo go.sum")
	}

	release.grid.NewButton("Check Ready", func() {
		buttonDisable()
		defer buttonEnable()
		goSumS := release.current.getGoSumStatus()
		dirtyS := release.current.dirtyLabel.String()
		lastS := release.current.status.GetLastTagVersion()
		if CheckReady() {
			log.Info("repo is ready", release.current.String(), goSumS, dirtyS, lastS)
			return
		} else {
			log.Info("\trepo is not ready", release.current.String(), goSumS, dirtyS, lastS)
		}
	})

	release.grid.NewButton("set ignore", func() {
		tmp := release.current.getGoSumStatus()
		log.Info("trying to set repo IGNORE is now =", tmp)
		release.current.setGoSumStatus("IGNORE")
		release.whitelist[release.current.String()] = release.current
	})

	release.checkSafeB = release.grid.NewButton("CheckSafeGoSumRemake()", func() {
		buttonDisable()
		release.current.status.CheckSafeGoSumRemake()
		release.current.status.MakeRedomod()
		if ok, _ := release.current.status.CheckGoSum(); ok {
			log.Info("repo has go.sum requirements that are clean")
		} else {
			log.Info("repo go.sum FAILED")
		}
		buttonEnable()
	})

	release.checkGoSumB = release.grid.NewButton("CheckGoSum()", func() {
		buttonDisable()
		if ok, _ := release.current.status.CheckGoSum(); ok {
			log.Info("repo has go.sum requirements that are clean")
			// release.current.setGoSumStatus("CLEAN")
			release.status.SetValue("GOOD")
			release.notes.SetValue("CheckGoSum() does not seem to lie")
		}
		buttonEnable()
	})

	release.grid.NewButton("CheckPrimativeGoMod()", func() {
		tmp := release.current.String()
		log.Info("Run CheckGoSum on repo:", tmp)
		if release.current.status.CheckPrimativeGoMod() {
			log.Info("repo has PRIMATIVE go.mod")
		} else {
			log.Info("repo go.mod requies checking for a go.sum")
		}

	})

	release.grid.NewButton("reset with scanGoSum()", func() {
		buttonDisable()
		// do an initial scan of all the repos
		scanGoSum()
		buttonEnable()
	})
	release.makeRedomodB = release.grid.NewButton("make redomod", func() {
		buttonDisable()
		release.current.status.MakeRedomod()
		buttonEnable()
	})
	release.goGetB = release.grid.NewButton("go get -v -u .", func() {
		buttonDisable()
		err, output := release.current.status.RunCmd([]string{"go", "get", "-v", "-u", "."})
		log.Warn(output)
		if err == nil {
			log.Warn("go get worked. recheck go.sum")
		} else {
			log.Warn("go get failed")
		}
		buttonEnable()
	})
	release.goGetB = release.grid.NewButton("SetMainWorkingName(guimaster)", func() {
		buttonDisable()
		release.current.status.SetMainWorkingName("guimaster")
		buttonEnable()
	})
}

func goodCheckGoSum() bool {
	tmp := release.current.String()
	log.Info("goodCheckGoSum() START on repo:", tmp, "STATUS =", release.current.getGoSumStatus())
	/*
	 */

	var maybe bool = true
	goConfig := release.current.status.GetGoDeps()
	for depname, version := range goConfig {
		log.Info("Checking repo deps:", depname, version)
		repo, ok := me.allrepos[depname]
		if ok {
			goSumS := repo.getGoSumStatus()
			if goSumS == "WHITELIST" {
				continue
			}
			lastS := repo.status.GetLastTagVersion()
			targetS := repo.targetVersion.String()
			log.Info("   repo deps:", depname, version, "vs", goSumS, lastS, targetS)
			if lastS != targetS {
				maybe = false
			}
		} else {
			log.Info("   repo deps: IGNORE", depname, version)
		}
	}
	if maybe {
		log.Info("SUCCEEDED.", release.current.String())
		log.Info("SUCCEEDED. goSumStatus.String() =", release.current.goSumStatus.String())
		log.Info("SUCCEEDED. MAYBE. try it again get go.sum requirements")
		return true
	} else {
		log.Info("FAILED. move on")
	}
	return false
}

func buttonDisable() {
	me.Disable()
}

func buttonEnable() {
	me.Enable()
}

func findDirty2() bool {
	log.Info("findDirty2() START")
	for _, repo := range me.allrepos {
		goSumS := repo.getGoSumStatus()
		dirtyS := repo.dirtyLabel.String()

		if goSumS == "IGNORE" {
			continue
		}

		if goSumS == "DIRTY 2" {
			log.Info("repo DIRTY 2", repo.String(), goSumS, dirtyS)
			if setCurrentRepo(repo, "dirty 2", "check manually I guess") {
				return true
			}
			return true
		} else {
			log.Info("repo not DIRTY 2", repo.String(), goSumS, dirtyS)
		}
	}
	log.Info("findDirty2() END")
	return false
}

func setCurrentRepo(newcur *repo, s string, note string) bool {
	if newcur.status.ReadOnly() {
		return false
	}

	release.repo.SetValue(newcur.status.String())
	release.status.SetValue(s)
	release.notes.SetValue(note)
	release.current = newcur
	release.version.SetText(release.current.targetVersion.String())
	release.releaseVersionB.SetText("release version " + release.current.targetVersion.String())
	release.unreleaseB.SetText("un-release version " + release.current.targetVersion.String())
	release.openrepo.Enable()

	/*
		if newcur.status.ReadOnly() {
			release.readOnly.SetValue("true ro")
		} else {
			release.readOnly.SetValue("false ro")
		}
	*/
	return true
}

func scanForReady() bool {
	for _, repo := range me.allrepos {
		goSumS := repo.getGoSumStatus()
		dirtyS := repo.dirtyLabel.String()
		log.Info("findNextDirty()", repo.String(), goSumS, dirtyS)

		if whitelist(repo.String()) {
			log.Info("found WHITELIST", repo.String())
			repo.setGoSumStatus("WHITELIST")
			continue
		}

		log.Info("scan for Ready: found a repo")
		if setCurrentRepo(repo, "checking ready", "notsure") {
			CheckReady()
		}
	}
	return true
}

func findNextDirty(onlyKind string) bool {
	for key := range me.allrepos {
		repo := me.allrepos[key]
		goSumS := repo.getGoSumStatus()
		dirtyS := repo.dirtyLabel.String()

		log.Info("findNextDirty()", repo.String(), goSumS, dirtyS)
		if repo.status.ReadOnly() {
			log.Info("findNextDirty() skipping readonly")
			continue
		}
		if goSumS == "PRIMATIVE" {
			if setCurrentRepo(repo, "PRIMATIVE", "release new version") {
				if release.version.String() == release.current.status.GetLastTagVersion() {
					// everything is fine. the primative has already been released
				} else {
					return true
				}
			}
			continue
		} else {
			if onlyKind == "PRIMATIVE" {
				continue
			}
		}
		if goSumS == "IGNORE" {
			continue
		}
		if goSumS == "RELEASED" {
			continue
		}
		if goSumS == "WHITELIST" {
			continue
		}
		setCurrentRepo(repo, "REDO GOSUM", "try redoing the gosum")
		newgoSumS := release.current.getGoSumStatus()
		newdirtyS := release.current.dirtyLabel.String()
		newlastS := release.current.status.GetLastTagVersion()
		if goodCheckGoSum() {
			log.Info("findNextDirty() returning true from goodCheckGoSum()", newgoSumS, newgoSumS, newdirtyS, newlastS)
			return true
		} else {
			// continue
		}
		if repo.String() == "go.wit.com/log" {
			log.Info("findNextDirty() FOUND LOG", repo.String(), goSumS, dirtyS)
			log.Info("findNextDirty() FOUND LOG", repo.String(), goSumS, dirtyS)
			log.Info("findNextDirty() FOUND LOG", repo.String(), goSumS, dirtyS)
			log.Info("findNextDirty() FOUND LOG", repo.String(), goSumS, dirtyS)
			log.Info("findNextDirty() FOUND LOG", repo.String(), goSumS, dirtyS)
			log.Sleep(10)
			return true
		}
		continue
		if goSumS == "UNRELEASED" {
			if setCurrentRepo(repo, "UNRELEASED", "manually check go.sum") {
				return true
			}
			continue
		}
		if goSumS == "NOT READY" {
			if setCurrentRepo(repo, "NOT READY", "manually check go.sum") {
				return true
			}
			continue
		}
		if goSumS == "DIRTY 2" {
			continue
		}
		if goSumS == "BAD DEP" {
			// find out what kind of BAD DEP?
			continue
		}
		// latestversion := repo.status.GetLastTagVersion()
		if goSumS == "CLEAN" {
			// if it's clean here, then check and remake the go.sum file
			// then stop to commit the release version
			repo.checkSafeGoSumRemake()
			if repo.checkDirty() {
				dirtyS = repo.dirtyLabel.String()
			}
			if dirtyS == "PERFECT" {
				continue
			}
			if setCurrentRepo(repo, "clean", "check manually") {
				return true
			}
		}
		if goSumS == "DIRTY" {
			if ok, missing := repo.status.CheckGoSum(); ok {
				log.Info("repo has go.sum requirements that are clean")
				// repo.setGoSumStatus("CLEAN")
			} else {
				log.Info("DIRTY 2 repo has go.sum requirements that are screwed up. missing:", missing)
				repo.setGoSumStatus("DIRTY 2")
				continue
			}

			if setCurrentRepo(repo, "dirty", "commit changes") {
				return true
			}
		}
	}
	log.Info("tried to findNextDirty() but not sure what to do next")
	return false
}

func findNextRepo() bool {
	for _, repo := range me.allrepos {
		goSumS := repo.getGoSumStatus()
		dirtyS := repo.dirtyLabel.String()

		log.Info("findNextRepo()", repo.String(), goSumS, dirtyS)
		if goSumS == "IGNORE" {
			continue
		}
		if goSumS == "DIRTY 2" {
			continue
		}
		if goSumS == "BAD VERSION" {
			continue
		}
		if goSumS == "BAD DEP" {
			// find out what kind of BAD DEP?
			continue
		}
		// latestversion := repo.status.GetLastTagVersion()
		if goSumS == "CLEAN" {
			// if it's clean here, then check and remake the go.sum file
			// then stop to commit the release version
			repo.checkSafeGoSumRemake()
			if repo.checkDirty() {
				dirtyS = repo.dirtyLabel.String()
			}
			if dirtyS == "PERFECT" {
				continue
			}
			if setCurrentRepo(repo, "clean round 2", "check manually") {
				return true
			}
		}
		if goSumS == "DIRTY" {
			if ok, missing := repo.status.CheckGoSum(); ok {
				log.Info("repo has go.sum requirements that are clean")
				// repo.setGoSumStatus("CLEAN")
			} else {
				log.Info("DIRTY 3 repo has go.sum requirements that are screwed up. missing:", missing)
				repo.setGoSumStatus("DIRTY 3")
				continue
			}

			if setCurrentRepo(repo, "dirty", "commit changes") {
				return true
			}
		}
		if goSumS == "BAD" {
			// if it's clean here, then check and remake the go.sum file
			// then stop to commit the release version
			repo.checkSafeGoSumRemake()
			if repo.checkDirty() {
				dirtyS = repo.dirtyLabel.String()
			}
			if setCurrentRepo(repo, "bad", "redo go.sum") {
				return true
			}
		}
	}
	log.Info("tried to findNextRepo() but not sure what to do next")
	return false
}