package config

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/alibaba/git-repo-go/file"
	"github.com/alibaba/git-repo-go/path"
	"github.com/jiangxin/goconfig"
	log "github.com/jiangxin/multi-log"
)

const (
	gitExtraConfigVersion = "5"
	gitExtraConfigFile    = "gitconfig"
	cfgRepoConfigVersion  = "repo.configversion"
)

var (
	gitConfigExtension = `
# This file is generated by git-repo.
# DO NOT edit this file! Any modification will be overwritten.
#
# Command alias
[alias]
	br = branch
	ci = commit -s
	co = checkout
	cp = cherry-pick
	st = status
	logf = log --pretty=fuller
	logs = log --pretty=refs  --date=short
	pr = repo upload --single
	peer-review = repo upload --single
	review = repo upload --single
	download = repo download --single
[color]
	ui = auto
[core]
	# Do not quote path, show UTF-8 characters directly
	quotepath = false
[filter "keyword-subst"]
	clean = git repo filter --clean %f
	smudge = git repo filter --smudge %f
[merge]
	# Add at most 20 commit logs in merge log message
	log = true
[pretty]
	refs = format:%h (%s, %ad)
[rebase]
	# Run git rebase with --autosquash option
	autosquash = true
[repo]
	# Version of this git config extension
	configversion = ` + gitExtraConfigVersion + `
`
)

// CheckGitAlias checks if any alias command has been overridden.
func CheckGitAlias() {
	var aliasCommands = []string{
		"git-review",
		"git-pr",
		"git-peer-review",
	}

	for _, cmd := range aliasCommands {
		p, err := exec.LookPath(cmd)
		if err == nil {
			log.Warnf("you cannot use the git-repo alias command '%s', it is overrided by '%s' installed", cmd, p)
		}
	}
}

func saveExtraGitConfig() error {
	var (
		err error
	)

	configDir, err := GetConfigDir()
	if err != nil {
		return err
	}
	filename := filepath.Join(configDir, gitExtraConfigFile)

	dir := filepath.Dir(filename)
	lockfile := filename + ".lock"

	if _, err := os.Stat(dir); err != nil {
		err = os.MkdirAll(dir, 0755)
		if err != nil {
			return err
		}
	}

	file, err := file.New(lockfile).OpenCreateRewrite()
	if err != nil {
		return err
	}

	defer os.Remove(lockfile)

	_, err = file.WriteString(gitConfigExtension)
	if err != nil {
		file.Close()
		return fmt.Errorf("fail to write file %s: %s", lockfile, err)
	}
	file.Close()

	_, err = goconfig.Load(lockfile)
	if err != nil {
		return fmt.Errorf("bad git config in %s: %s", lockfile, err)
	}

	return os.Rename(lockfile, filename)
}

// InstallExtraGitConfig installs extra git config file if necessary.
func InstallExtraGitConfig() error {
	var err error

	globalConfig, err := goconfig.GlobalConfig()
	version := globalConfig.Get(cfgRepoConfigVersion)
	if version == gitExtraConfigVersion {
		return nil
	}

	log.Debugf("unmatched git config version: %s != %s", version, gitExtraConfigVersion)
	found := false
	configDir, err := GetConfigDir()
	if err != nil {
		return err
	}
	absExtraConfigFile := filepath.Join(configDir, gitExtraConfigFile)
	for _, p := range globalConfig.GetAll("include.path") {
		p, _ = path.Abs(p)
		if p == absExtraConfigFile {
			found = true
			break
		}
	}
	if !found {
		// git 1.7.10 does not support relative path (~/.git-repo) as include.path.
		cmds := []string{"git",
			"config",
			"--global",
			"--add",
			"include.path",
			absExtraConfigFile,
		}
		err = exec.Command(cmds[0], cmds[1:]...).Run()
		if err != nil {
			return err
		}
	}

	err = saveExtraGitConfig()
	if err != nil {
		return err
	}
	return nil
}
