package forgepb

// for golang repos, this is an attempt to build the package
//
//  Additions to 'go build' that are attempted here:
//
// * detect packages that are plugins
// * run autogenpb packages that have .proto protobuf files
// * use some 'standard' ldflags (VERISON, BUILDTIME)
//

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"go.wit.com/lib/gui/shell"
	"go.wit.com/lib/protobuf/gitpb"
	"go.wit.com/log"
)

func (f *Forge) Build(repo *gitpb.Repo, userFlags []string) error {
	return f.doBuild(repo, userFlags, "build")
}

func (f *Forge) Install(repo *gitpb.Repo, userFlags []string) error {
	return f.doBuild(repo, userFlags, "install")
}

func (f *Forge) doBuild(repo *gitpb.Repo, userFlags []string, goWhat string) error {
	if repo == nil {
		log.Warn("forge.doBuild repo == nil")
		return errors.New("forge.doBuild repo == nil")
	}
	if f.IsGoWork() {
		// when building using a go.work file, never use GO111MODULE=off
		os.Unsetenv("GO111MODULE")
	} else {
		// when building from ~/go/src, always use GO111MODULE=off
		os.Setenv("GO111MODULE", "off")
		defer os.Unsetenv("GO111MODULE")
	}

	if f.IsGoWork() {
		// there must be a valid go.mod file if compiling with go.work
		if err := repo.ValidGoSum(); err != nil {
			log.Warn("forge.doBuild() failed. run go-mod-clean here?")
			return err
		}
	}

	if repo.Exists(".forge") {
		log.Info("custom build instructions")
		data, _ := repo.ReadFile(".forge")
		log.Info(".forge =", string(data))
		log.Info("todo: do the custom build instructions")
		return nil
	}

	// if not GoPrimitive, autogen each dependent git repo
	if repo.GoDepsLen() != 0 {
		// build the protobuf files in all protobuf repos
		all := repo.GoDeps.SortByGoPath()
		for all.Scan() {
			t := all.Next()
			found := f.Repos.FindByGoPath(t.GetGoPath())
			if found.RepoType() == "protobuf" {
				if err := f.runAutogenpb(found); err != nil {
					return err
				}
			}
		}
	}

	// get the version
	version := repo.GetCurrentBranchVersion()

	if repo.CheckDirty() {
		version = version + "-dirty"
	}
	cmd := []string{"go"}
	if repo.RepoType() == "plugin" {
		if goWhat == "install" {
			log.Info("Can not go install plugins yet. just building to ~/go/lib/")
		}
		cmd = append(cmd, "build")
	} else {
		cmd = append(cmd, goWhat)
	}

	_, fname := filepath.Split(repo.FullPath)
	homeDir, _ := os.UserHomeDir()
	soname := fname + "." + version + ".so"
	linkname := fname + ".so"
	sopath := filepath.Join(homeDir, "go/lib/go-gui")
	// if this is a plugin, use buildmode=plugin
	if repo.RepoType() == "plugin" {
		if goWhat == "install" {
			fullname := filepath.Join(sopath, soname)
			cmd = append(cmd, "-buildmode=plugin", "-o", fullname)
		} else {
			cmd = append(cmd, "-buildmode=plugin", "-o", soname)
		}
	}
	cmd = append(cmd, "-v")

	// set standard ldflag options
	now := time.Now()
	datestamp := now.UTC().Format("2006/01/02_1504_UTC")
	// log.Info("datestamp =", datestamp)
	// add some standard golang flags
	ldflags := "-X main.VERSION=" + version + " "
	ldflags += "-X main.BUILDTIME=" + datestamp + " "
	ldflags += "-X main.GUIVERSION=" + version + "" // todo: git this from the filesystem
	cmd = append(cmd, "-ldflags", ldflags)

	// add any flags from the command line
	// this might not actually work
	// todo: test this
	for _, flag := range userFlags {
		cmd = append(cmd, "-ldflags", "-X "+flag)
	}

	testenv := os.Getenv("GO111MODULE")
	if testenv == "off" {
		log.Info("GO111MODULE=off", "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
	} else {
		log.Info("GO111MODULE=", testenv, "f.goWork =", f.IsGoWork(), "f.gosrc =", f.GetGoSrc())
	}
	log.Info("running:", repo.FullPath)
	log.Info("running:", cmd)
	result := repo.RunRealtime(cmd)
	if result.Exit != 0 {
		// build failed
		log.DaemonMode(true)
		log.Info(strings.Join(result.Stdout, "\n"))
		log.Info(strings.Join(result.Stderr, "\n"))
		log.Info("result.Error =", result.Error)
		log.Info("result.Exit =", result.Exit)
		log.DaemonMode(false)
		log.Warn("go build failed", cmd)
		/*
			pwd, _ := os.Getwd()
			log.Warn("go build pwd", pwd)
			res2 := shell.RunEcho(cmd)
			if res2.Exit == 0 {
				log.Info("again failed", res2.Exit)
				log.Info("again failed cmd", strings.Join(cmd, "a"))
				log.Info("again failed", strings.Join(res2.Stdout, "\n"))
			}
		*/
		return errors.New("go " + goWhat + " failed: " + fmt.Sprint(result.Error))
	}
	// make symlinks
	if repo.RepoType() == "plugin" {
		cmd := []string{"ln", "-sf", soname, linkname}
		if goWhat == "install" {
			shell.PathRun(sopath, cmd)
		} else {
			repo.Run(cmd)
		}
	}
	log.Info(strings.Join(result.Stdout, "\n"))
	return nil
}

func (f *Forge) runAutogenpb(repo *gitpb.Repo) error {
	// log.Info("run autogenpb here:", repo.GetGoPath())
	files, err := repo.GetProtoFiles()
	if err != nil {
		log.Info("gitpb.GetProtoFiles()", err)
		return err
	}
	for _, s := range files {
		_, filename := filepath.Split(s)
		pbfile := strings.TrimSuffix(filename, ".proto") + ".pb.go"
		cmd := []string{"autogenpb", "--proto", filename}
		if repo.Exists(pbfile) {
			// log.Info("skip running:", cmd)
			continue
		}
		log.Info("running:", cmd)
		r := repo.RunRealtime(cmd)
		if r.Error != nil {
			log.Warn("")
			log.Warn("autogenpb error", r.Error)
			log.Warn("")
			log.Warn("You might be missing autogenpb?")
			log.Warn("")
			log.Warn("To install it run: go install go.wit.com/apps/autogenpb@latest")
			log.Warn("")
			log.Sleep(2)
			return r.Error
		}
	}
	return nil
}