package log

import (
	"fmt"
	"io"
	"net/url"
	"strings"

	"github.com/sirupsen/logrus"
)

//PiperLogFormatter is the custom formatter of piper
type PiperLogFormatter struct {
	logrus.TextFormatter
	logFormat string
}

const (
	logFormatPlain         = "plain"
	logFormatDefault       = "default"
	logFormatWithTimestamp = "timestamp"
)

//Format the log message
func (formatter *PiperLogFormatter) Format(entry *logrus.Entry) (bytes []byte, err error) {
	message := ""

	stepName := entry.Data["stepName"]
	if stepName == nil {
		stepName = "(noStepName)"
	}

	errorMessageSnippet := ""
	if entry.Data[logrus.ErrorKey] != nil {
		errorMessageSnippet = fmt.Sprintf(" - %s", entry.Data[logrus.ErrorKey])
	}

	level, _ := entry.Level.MarshalText()
	levelString := string(level)
	if levelString == "warning" {
		levelString = "warn"
	}

	switch formatter.logFormat {
	case logFormatDefault:
		message = fmt.Sprintf("%-5s %-6s - %s%s\n", levelString, stepName, entry.Message, errorMessageSnippet)
	case logFormatWithTimestamp:
		message = fmt.Sprintf("%s %-5s %-6s %s%s\n", entry.Time.Format("15:04:05"), levelString, stepName, entry.Message, errorMessageSnippet)
	case logFormatPlain:
		message = fmt.Sprintf("%s%s\n", entry.Message, errorMessageSnippet)
	default:
		formattedMessage, err := formatter.TextFormatter.Format(entry)
		if err != nil {
			return nil, err
		}
		message = string(formattedMessage)
	}

	for _, secret := range secrets {
		message = strings.Replace(message, secret, "****", -1)
	}

	return []byte(message), nil
}

// LibraryRepository that is passed into with -ldflags
var LibraryRepository string
var LibraryName string
var logger *logrus.Entry
var secrets []string

// Entry returns the logger entry or creates one if none is present.
func Entry() *logrus.Entry {
	if logger == nil {
		logger = logrus.WithField("library", LibraryRepository)
		logger.Logger.SetFormatter(&PiperLogFormatter{})
	}

	return logger
}

// Writer returns an io.Writer into which a tool's output can be redirected.
func Writer() io.Writer {
	return &logrusWriter{logger: Entry()}
}

// SetVerbose sets the log level with respect to verbose flag.
func SetVerbose(verbose bool) {
	if verbose {
		//Logger().Debugf("logging set to level: %s", level)
		logrus.SetLevel(logrus.DebugLevel)
	}
}

// SetFormatter specifies the log format to use for piper's output
func SetFormatter(logFormat string) {
	Entry().Logger.SetFormatter(&PiperLogFormatter{logFormat: logFormat})
}

// SetStepName sets the stepName field.
func SetStepName(stepName string) {
	logger = Entry().WithField("stepName", stepName)
}

// DeferExitHandler registers a logrus exit handler to allow cleanup activities.
func DeferExitHandler(handler func()) {
	logrus.DeferExitHandler(handler)
}

// RegisterHook registers a logrus hook
func RegisterHook(hook logrus.Hook) {
	logrus.AddHook(hook)
}

// RegisterSecret registers a value which should be masked in every log message
func RegisterSecret(secret string) {
	if len(secret) > 0 {
		secrets = append(secrets, secret)
		encoded := url.QueryEscape(secret)
		if secret != encoded {
			secrets = append(secrets, encoded)
		}
	}
}
