//go:build dist

package main

import (
	"archive/tar"
	"compress/gzip"
	"context"
	"crypto/sha512"
	_ "embed"
	"encoding/hex"
	"fmt"
	"io"
	"io/fs"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"runtime"
	"strings"
)

//go:generate sh -c "git describe --tags > VERSION"
//go:embed VERSION
var version string

// getenv looks up an environment variable, and returns fallback if it is unset.
func getenv(key, fallback string) string {
	if v, ok := os.LookupEnv(key); ok {
		return v
	}
	return fallback
}

// mustRun runs a command with the current process's environment and panics
// on error or non-zero exit code.
func mustRun(ctx context.Context, env []string, name string, arg ...string) {
	cmd := exec.CommandContext(ctx, name, arg...)
	if env != nil {
		cmd.Env = append(cmd.Environ(), env...)
	}
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		panic(err)
	}
}

//go:embed comp/_hakurei
var comp []byte

func main() {
	log.SetFlags(0)
	log.SetPrefix("")
	log.SetOutput(os.Stdout)

	verbose := os.Getenv("VERBOSE") != ""
	runTests := os.Getenv("HAKUREI_DIST_MAKE") == ""
	version = getenv("HAKUREI_VERSION", strings.TrimSpace(version))
	prefix := getenv("PREFIX", "/usr/local")
	destdir := getenv("DESTDIR", "dist")

	if verbose {
		log.Println()
	}

	if err := os.MkdirAll(destdir, 0755); err != nil {
		log.Fatal(err)
	}
	s, err := os.MkdirTemp(destdir, ".dist.*")
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		var code int

		if err = os.RemoveAll(s); err != nil {
			code = 1
			log.Println(err)
		}

		if r := recover(); r != nil {
			code = 1
			log.Println(r)
		}

		os.Exit(code)
	}()

	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()

	verboseFlag := "-v"
	if !verbose {
		verboseFlag = "-buildvcs=false"
	}

	log.Printf("Building hakurei %s for %s/%s.", version, runtime.GOOS, runtime.GOARCH)
	mustRun(ctx, nil, "go", "generate", "./...")
	mustRun(
		ctx, nil, "go", "build",
		"-trimpath",
		verboseFlag, "-o", s,
		"-ldflags=-s -w "+
			"-buildid= -linkmode external -extldflags=-static "+
			"-X hakurei.app/internal/info.buildVersion="+version+" "+
			"-X hakurei.app/internal/info.hakureiPath="+prefix+"/bin/hakurei "+
			"-X hakurei.app/internal/info.hsuPath="+prefix+"/bin/hsu",
		"./cmd/hakurei",
		"./cmd/sharefs",
	)

	log.Printf("Building cmd/hsu for %s/%s.", runtime.GOOS, runtime.GOARCH)
	mustRun(
		ctx, []string{"CGO_ENABLED=0"}, "go", "build",
		"-trimpath",
		verboseFlag, "-o", s,
		"-ldflags=-s -w "+
			"-buildid= "+
			"-X main.hakureiPath="+prefix+"/bin/hakurei",
		"./cmd/hsu",
	)

	log.Println()
	if runTests {
		log.Println("##### Testing Hakurei.")
		mustRun(
			ctx, nil, "go", "test",
			"-ldflags=-buildid= -linkmode external -extldflags=-static",
			"./...",
		)
		log.Println()
	}

	log.Println("##### Creating distribution.")
	const suffix = ".tar.gz"
	distName := "hakurei-" + version + "-" + runtime.GOARCH
	var f *os.File
	if f, err = os.OpenFile(
		filepath.Join(s, distName+suffix),
		os.O_CREATE|os.O_EXCL|os.O_WRONLY,
		0644,
	); err != nil {
		panic(err)
	}
	defer func() {
		if f == nil {
			return
		}
		if err = f.Close(); err != nil {
			log.Println(err)
		}
	}()

	h := sha512.New()
	gw, _ := gzip.NewWriterLevel(io.MultiWriter(f, h), gzip.BestCompression)
	tw := tar.NewWriter(gw)

	mustWriteHeader := func(name string, size int64, mode os.FileMode) {
		header := tar.Header{
			Name:  filepath.Join(distName, name),
			Size:  size,
			Mode:  int64(mode),
			Uname: "root",
			Gname: "root",
		}

		if mode&os.ModeDir != 0 {
			header.Typeflag = tar.TypeDir
			fmt.Printf("%s %s\n", mode, name)
		} else {
			header.Typeflag = tar.TypeReg
			fmt.Printf("%s %s (%d bytes)\n", mode, name, size)
		}

		if err = tw.WriteHeader(&header); err != nil {
			panic(err)
		}
	}

	mustWriteFile := func(name string, data []byte, mode os.FileMode) {
		mustWriteHeader(name, int64(len(data)), mode)
		if mode&os.ModeDir != 0 {
			return
		}
		if _, err = tw.Write(data); err != nil {
			panic(err)
		}
	}

	mustWriteFromPath := func(dst, src string, mode os.FileMode) {
		var r *os.File
		if r, err = os.Open(src); err != nil {
			panic(err)
		}

		var fi os.FileInfo
		if fi, err = r.Stat(); err != nil {
			_ = r.Close()
			panic(err)
		}

		if mode == 0 {
			mode = fi.Mode()
		}

		mustWriteHeader(dst, fi.Size(), mode)
		if _, err = io.Copy(tw, r); err != nil {
			_ = r.Close()
			panic(err)
		} else if err = r.Close(); err != nil {
			panic(err)
		}
	}

	mustWriteFile(".", nil, fs.ModeDir|0755)
	mustWriteFile("comp/", nil, os.ModeDir|0755)
	mustWriteFile("comp/_hakurei", comp, 0644)
	mustWriteFile("install.sh", []byte(`#!/bin/sh -e
cd "$(dirname -- "$0")"

install -vDm0755 "bin/hakurei" "${DESTDIR}`+prefix+`/bin/hakurei"
install -vDm0755 "bin/sharefs" "${DESTDIR}`+prefix+`/bin/sharefs"

install -vDm4511 "bin/hsu" "${DESTDIR}`+prefix+`/bin/hsu"
if [ ! -f "${DESTDIR}/etc/hsurc" ]; then
    install -vDm0400 "hsurc.default" "${DESTDIR}/etc/hsurc"
fi

install -vDm0644 "comp/_hakurei" "${DESTDIR}`+prefix+`/share/zsh/site-functions/_hakurei"
`), 0755)

	mustWriteFromPath("LICENSE", "LICENSE", 0)
	mustWriteFile("hsurc.default", []byte("1000 0"), 0400)
	mustWriteFromPath("bin/hsu", filepath.Join(s, "hsu"), 04511)
	for _, name := range []string{
		"hakurei",
		"sharefs",
	} {
		mustWriteFromPath(
			filepath.Join("bin", name),
			filepath.Join(s, name),
			0,
		)
	}

	if err = tw.Close(); err != nil {
		panic(err)
	} else if err = gw.Close(); err != nil {
		panic(err)
	} else if err = f.Close(); err != nil {
		panic(err)
	}
	f = nil

	if err = os.WriteFile(
		filepath.Join(destdir, distName+suffix+".sha512"),
		append(hex.AppendEncode(nil, h.Sum(nil)), "  "+distName+suffix+"\n"...),
		0644,
	); err != nil {
		panic(err)
	}
	if err = os.Rename(
		filepath.Join(s, distName+suffix),
		filepath.Join(destdir, distName+suffix),
	); err != nil {
		panic(err)
	}
}
