package core

import (
	"bytes"
	"os"
	"sync"
	"text/template"

	"github.com/mgutz/ansi"
)

// DisableColor can be used to make testing reliable
var DisableColor = false

var TemplateFuncsWithColor = map[string]interface{}{
	// Templates with Color formatting. See Documentation: https://github.com/mgutz/ansi#style-format
	"color": ansi.ColorCode,
}

var TemplateFuncsNoColor = map[string]interface{}{
	// Templates without Color formatting. For layout/ testing.
	"color": func(color string) string {
		return ""
	},
}

// envColorDisabled returns if output colors are forbid by environment variables
func envColorDisabled() bool {
	return os.Getenv("NO_COLOR") != "" || os.Getenv("CLICOLOR") == "0"
}

// envColorForced returns if output colors are forced from environment variables
func envColorForced() bool {
	val, ok := os.LookupEnv("CLICOLOR_FORCE")
	return ok && val != "0"
}

// RunTemplate returns two formatted strings given a template and
// the data it requires. The first string returned is generated for
// user-facing output and may or may not contain ANSI escape codes
// for colored output. The second string does not contain escape codes
// and can be used by the renderer for layout purposes.
func RunTemplate(tmpl string, data interface{}) (string, string, error) {
	tPair, err := GetTemplatePair(tmpl)
	if err != nil {
		return "", "", err
	}
	userBuf := bytes.NewBufferString("")
	err = tPair[0].Execute(userBuf, data)
	if err != nil {
		return "", "", err
	}
	layoutBuf := bytes.NewBufferString("")
	err = tPair[1].Execute(layoutBuf, data)
	if err != nil {
		return userBuf.String(), "", err
	}
	return userBuf.String(), layoutBuf.String(), err
}

var (
	memoizedGetTemplate = map[string][2]*template.Template{}

	memoMutex = &sync.RWMutex{}
)

// GetTemplatePair returns a pair of compiled templates where the
// first template is generated for user-facing output and the
// second is generated for use by the renderer. The second
// template does not contain any color escape codes, whereas
// the first template may or may not depending on DisableColor.
func GetTemplatePair(tmpl string) ([2]*template.Template, error) {
	memoMutex.RLock()
	if t, ok := memoizedGetTemplate[tmpl]; ok {
		memoMutex.RUnlock()
		return t, nil
	}
	memoMutex.RUnlock()

	templatePair := [2]*template.Template{nil, nil}

	templateNoColor, err := template.New("prompt").Funcs(TemplateFuncsNoColor).Parse(tmpl)
	if err != nil {
		return [2]*template.Template{}, err
	}

	templatePair[1] = templateNoColor

	envColorHide := envColorDisabled() && !envColorForced()
	if DisableColor || envColorHide {
		templatePair[0] = templatePair[1]
	} else {
		templateWithColor, err := template.New("prompt").Funcs(TemplateFuncsWithColor).Parse(tmpl)
		templatePair[0] = templateWithColor
		if err != nil {
			return [2]*template.Template{}, err
		}
	}

	memoMutex.Lock()
	memoizedGetTemplate[tmpl] = templatePair
	memoMutex.Unlock()
	return templatePair, nil
}
