package giq

import (
	"bytes"
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func stringifyOutput(s []byte) string {
	return strings.TrimSpace(string(s))
}

func (g *Sysgit) Whoami() string {
	return "I am Sysgit!"
}

func (g *Sysgit) AddBareWorktree(gitRepoPath, gitIndexPath, gitWorktree, gexe string) {
	tmpRepoPath := filepath.Join(os.TempDir(), gitWorktree)
	if _, err := os.Stat(tmpRepoPath); err == nil {
		err := os.RemoveAll(tmpRepoPath)
		check(err)
	}
	err := os.MkdirAll(tmpRepoPath, 0755)
	check(err)

	gitAddWorktree := exec.Command(gexe, "-C", gitRepoPath, "worktree", "add", "--detach", tmpRepoPath)
	gitAddWorktree.Env = os.Environ()
	gitAddWorktree.Env = append(gitAddWorktree.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
	err = gitAddWorktree.Run()
	check(err)
}

func (g *Sysgit) RemoveBareWorktree(gitRepoPath, gitIndexPath, gitWorktree, gexe string) {
	gitRemoveWorktree := exec.Command(gexe, "-C", gitRepoPath, "worktree", "remove", gitWorktree)
	gitRemoveWorktree.Env = os.Environ()
	gitRemoveWorktree.Env = append(gitRemoveWorktree.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
	gitRemoveWorktree.Run()
}

func isPublished(gitRepoPath, gitIndexPath, prevCommit, lastCommit string) bool {
	var sout bytes.Buffer
	//var serr bytes.Buffer
	commits := exec.Command("git", "-C", gitRepoPath, "rev-list", fmt.Sprintf("%s..%s", prevCommit, lastCommit))
	commits.Env = os.Environ()
	commits.Env = append(commits.Env, fmt.Sprintf("GIT_INDEX_FILE=%s%s", gitRepoPath, gitIndexPath))
	commits.Stdout = &sout
	//commits.Stderr = &serr
	err := commits.Run()
	check(err)
	for _, commit := range strings.Fields(sout.String()) {
		committedFiles, err := exec.Command("git", "-C", gitRepoPath, "diff-tree", "--no-commit-id", "--name-only", commit).Output()
		check(err)
		for _, commitFile := range strings.Fields(string(committedFiles)) {
			if commitFile == "PUBLISH.trigger.md" {
				return true
			}
		}

		commitMessages, err := exec.Command("git", "-C", gitRepoPath, "show", "-s", "--format=%B", commit).Output()
		check(err)

		if strings.Contains(string(commitMessages), "!publish!") {
			return true
		}
	}
	return false
}

func (g *Sysgit) GitStruct(hookPath, hookContext, hookStdinput, gexe string) Git {
	var git Git

	repositoryPath, err := exec.Command(gexe, "-C", hookPath, "rev-parse", "--git-dir").Output()
	check(err)
	repoPath := stringifyOutput(repositoryPath)
	gitBareOutput, err := exec.Command(gexe, "-C", repoPath, "rev-parse", "--is-bare-repository").Output()
	check(err)
	gitBare := stringifyOutput(gitBareOutput)
	if gitBare == "true" {
		git.IsBare = true
	} else {
		git.IsBare = false
		repoPath = strings.Replace(stringifyOutput(repositoryPath), ".git", "", 1)
		check(err)
	}

	gitLastCommit, err := exec.Command(gexe, "-C", repoPath, "rev-parse", "HEAD").Output()
	check(err)
	gitPreviousCommit, err := exec.Command(gexe, "-C", repoPath, "rev-parse", "HEAD^").Output()
	check(err)
	gitBranch, err := exec.Command(gexe, "-C", repoPath, "branch", "-a", "--contains", "HEAD").Output()
	check(err)

	rand.Seed(time.Now().UnixNano())
	git.Worktree = fmt.Sprintf("sandpoints_repo_%06d", rand.Intn(100001))
	git.TmpRepoPath = filepath.Join(os.TempDir(), git.Worktree)

	git.RepoPath = repoPath
	git.LastCommit = stringifyOutput(gitLastCommit)
	git.PreviousCommit = stringifyOutput(gitPreviousCommit)
	git.Branch = strings.Replace(stringifyOutput(gitBranch), "* ", "", 1)
	git.IndexPath = filepath.Join("/", ".git", "index")
	if git.IsBare {
		git.IndexPath = filepath.Join("/", "index")
	}
	git.Publish = isPublished(git.RepoPath, git.IndexPath, git.PreviousCommit, git.LastCommit)
	return git
}
