//go:build go1.20
// +build go1.20

// protobuf the way I am using them, require GO 1.20. I think. I could be wrong.

// go:generate go-mod-clean
// go:generate autogenpb auto.proto

package main

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

	"github.com/alexflint/go-arg"
	"github.com/go-cmd/cmd"
	"go.wit.com/lib/gui/shell"
	"go.wit.com/log"
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
)

// sent via -ldflags
var VERSION string
var BUILDTIME string

var sortmap map[string]string
var marshalKeys []string
var uniqueKeys []string

func main() {
	pp := arg.MustParse(&argv)

	var pb *Files
	pb = new(Files)

	// you need a proto file
	if argv.Proto == "" {
		log.Info("you must provide --proto <filename>")
		pp.WriteHelp(os.Stdout)
		os.Exit(-1)
	}

	if !shell.Exists(argv.Proto) {
		log.Info("protobuf", argv.Proto, "is missing")
		if !argv.DryRun {
			os.Exit(-1)
		}
	}

	if !strings.HasSuffix(argv.Proto, ".proto") {
		log.Info("protobuf", argv.Proto, "must end in .proto")
		os.Exit(-1)
	}

	f := new(File)
	pb.Files = append(pb.Files, f)
	f.Filename = argv.Proto

	// todo, look for go.work files
	if argv.GoSrc == "" {
		homeDir, _ := os.UserHomeDir()
		argv.GoSrc = filepath.Join(homeDir, "go/src")
	}

	if argv.GoPath == "" {
		pwd, _ := os.Getwd()
		argv.GoPath = strings.TrimPrefix(pwd, argv.GoSrc)
		argv.GoPath = strings.Trim(argv.GoPath, "/")
	}
	log.Info(argv.GoSrc, argv.GoPath)

	pwd, _ := os.Getwd()
	log.Info("pwd = ", pwd)

	if !shell.Exists("go.sum") {
		shell.RunQuiet([]string{"go-mod-clean"})
		if !shell.Exists("go.sum") {
			shell.RunQuiet([]string{"go", "mod", "init"})
			shell.RunQuiet([]string{"go", "mod", "tidy"})
			shell.RunQuiet([]string{"go", "mod", "edit", "-go=1.18"}) // TODO: pass this as ENV. verify protobuf version needed
		}
	}

	var packageName string
	var result cmd.Status
	var cmd []string
	if argv.Package == "" {
		// TODO: switch to using forgepb (expose the functions/logic from forgepb directly
		// it could be a bad idea to use forge.Init() here as forge needs this to build
		// switch to forgepb
		os.Setenv("GO111MODULE", "off") // keeps go list working if go version is back versioned for compatability
		cmd = []string{"go", "list", "-f", "'{{.Name}}'"}
		result = shell.RunQuiet(cmd)
		os.Unsetenv("GO111MODULE")

		packageName = strings.Join(result.Stdout, "\n")
		packageName = strings.TrimSpace(packageName)
		packageName = strings.Trim(packageName, "'")
		// log.Info("packageName == ", packageName)
	} else {
		packageName = argv.Package
	}
	f.Package = packageName

	protobase := strings.TrimSuffix(argv.Proto, ".proto")
	f.Filebase = protobase

	// parse sort & marshal options from the .proto file
	// this goes through the .proto files and looks
	// for `autogenpb: ` lines
	if err := pb.protoParse(f); err != nil {
		log.Info("autogenpb parse error:", err)
		os.Exit(-1)
	}

	if argv.DryRun {
		os.Exit(0)
	}

	// try to make foo.pb.go with protoc if it's not here
	// this is helpful because the protoc-gen-go lines
	// are also annoying to code by hand

	f.Pbfilename = f.Filebase + ".pb.go"
	// try to create the foo.pb.go file using protoc if it is not there
	if !shell.Exists(f.Pbfilename) {
		if err := pb.protocBuild(f); err != nil {
			badExit(err)
		}

	}

	// try to add the Mutex to the pb.go file
	if err := pb.addMutex(f); err != nil {
		badExit(err)
	}

	// if foo.pb.go still doesn't exist, protoc failed
	if !shell.Exists(f.Pbfilename) {
		log.Info("protoc build error.", f.Pbfilename)
		badExit(errors.New("failed to be created with protoc and proto-gen-go"))
	}

	pb.marshal(f)

	// this should be garbage soon
	sortmap = make(map[string]string)
	sortmap["package"] = packageName
	sortmap["protofile"] = argv.Proto
	sortmap["protobase"] = protobase
	if argv.LoBase == "" {
		// if not set, assumed to be protobase
		sortmap["base"] = protobase
	} else {
		sortmap["base"] = argv.LoBase
	}
	sortmap["lock"] = sortmap["base"] + "sMu" // is nonglobal and plural
	if argv.UpBase == "" {
		sortmap["Base"] = cases.Title(language.English, cases.NoLower).String(protobase)
		sortmap["Bases"] = sortmap["Base"] + "s"
	} else {
		sortmap["Base"] = argv.UpBase
		sortmap["Bases"] = sortmap["Base"] + "s"
	}

	// pb.makeSortfile(f)
	pb.makeNewSortfile(f)
}

func okExit(s string) {
	log.Info("autogenpb ok", s)
	os.Exit(0)
}

func badExit(err error) {
	log.Info("autogenpb error:", err)
	os.Exit(-1)
}