package sync

import (
	"os"
	"path"
	"strings"

	"git.csclub.uwaterloo.ca/public/merlin/config"
)

func cscTraceArchLinux(repo *config.Repo, destPath string) []string {
	args := []string{
		"curl",
		"-s", repo.TraceUrl,
		"-o", destPath,
	}

	return args
}

// return false iff both files exist, are non-empty, and have the same contents
func cscTraceArchLinuxDiff(repo *config.Repo, destPath string) bool {
	readFile := func(file string) string {
		f, err := os.ReadFile(file)
		if err != nil {
			repo.Logger.Debug(err.Error())
			return ""
		}
		return strings.TrimSpace(string(f))
	}

	file1 := readFile(destPath)
	file2 := readFile(path.Join(buildDownloadDir(repo), "lastupdate"))

	if file1 == "" || file2 == "" {
		return true
	}
	return file1 != file2
}

func cscTraceArchLinuxUpdate(repo *config.Repo) (args []string) {
	rsyncDir := repo.RsyncDir
	localDir := repo.LocalDir

	repo.RsyncDir = repo.RsyncDir + "/lastsync"
	repo.LocalDir = repo.LocalDir + "/lastsync"

	args = cscSyncArchLinux(repo)

	repo.RsyncDir = rsyncDir
	repo.LocalDir = localDir

	return args
}

func cscTraceDebian(repo *config.Repo, destPath string) []string {
	args := []string{
		"nice", "rsync", "-tv", "-4",
		"--address=" + config.Conf.IPv4Address,
		"--quiet",
		// "--log-file=" + repo.RepoLogFile,
		repo.RsyncHost + "::" + path.Join(repo.RsyncDir, "project/trace", repo.TraceUrl),
		destPath,
	}

	return args
}

// return false iff both files exist and were modified at the same time
func cscTraceDebianDiff(repo *config.Repo, destPath string) bool {
	statFile := func(file string) int64 {
		f, err := os.Stat(file)
		if err != nil {
			repo.Logger.Debug(err.Error())
			return 0
		}

		return f.ModTime().Unix()
	}

	file1 := statFile(destPath)
	file2 := statFile(path.Join(buildDownloadDir(repo), "project/trace", "lastupdate"))

	if file1 == 0 || file2 == 0 {
		return true
	}

	return file1 != file2
}

// some repos want us to check a last modification time file before performing a full sync
// returns a status to update the original sync's status along with if the sync should continue
func checkIfSyncNeeded(repo *config.Repo) (status int, continueSync bool) {
	// default return values are the default status and to continue the sync
	status = config.FAILURE
	continueSync = true

	// create a temp file
	temp, err := os.CreateTemp("/tmp", ".tmp.merlin."+repo.Name+"-")
	if err != nil {
		repo.Logger.Error(err.Error())
		return
	}
	temp.Close()
	defer os.Remove(temp.Name())

	// get the trace command
	var args []string
	switch repo.SyncType {
	case "csc-sync-archlinux":
		args = cscTraceArchLinux(repo, temp.Name())
	case "csc-sync-debian":
		args = cscTraceDebian(repo, temp.Name())
	default:
		repo.Logger.Error("Trace files are not implemented for sync type '" + repo.SyncType + "'")
		return
	}

	ch := spawnProcess(repo, args)
	if ch == nil {
		// spawnSyncProcess will have already logged error
		return
	}

	cmd := <-ch
	if cmd.ProcessState.ExitCode() != 0 {
		// if the process was terminated then don't continue to sync
		if cmd.ProcessState.ExitCode() == -1 {
			return config.TERMINATED, false
		}
		return
	}

	filesDiffer := true
	switch repo.SyncType {
	case "csc-sync-archlinux":
		filesDiffer = cscTraceArchLinuxDiff(repo, temp.Name())
	case "csc-sync-debian":
		filesDiffer = cscTraceDebianDiff(repo, temp.Name())
	}

	// if the files are the same then don't continue to sync
	if !filesDiffer {
		repo.Logger.Debug("No changes found; no sync made")

		// archlinux wants to sync a "lastsync" file even if lastupdate prevents sync
		var args []string
		switch repo.SyncType {
		case "csc-sync-archlinux":
			args = cscTraceArchLinuxUpdate(repo)
		default:
			return config.SUCCESS, false
		}

		ch := spawnProcess(repo, args)
		if ch == nil {
			// spawnSyncProcess will have already logged error
			return
		}

		cmd := <-ch
		if cmd.ProcessState.ExitCode() != 0 {
			// if the process was terminated then don't continue to sync
			if cmd.ProcessState.ExitCode() == -1 {
				return config.TERMINATED, false
			}
			return
		}

		return config.SUCCESS, false
	}

	return
}
