package logger

import (
	"fmt"
	"io/ioutil"
	"log/syslog"
	"os"

	log "github.com/sirupsen/logrus"
	"gitlab.com/gitlab-org/gitlab-shell/internal/config"
)

type UTCFormatter struct {
	log.Formatter
}

func (u UTCFormatter) Format(e *log.Entry) ([]byte, error) {
	e.Time = e.Time.UTC()

	return u.Formatter.Format(e)
}

func configureLogFormat(cfg *config.Config) {
	if cfg.LogFormat == "json" {
		log.SetFormatter(UTCFormatter{&log.JSONFormatter{}})
	} else {
		log.SetFormatter(UTCFormatter{&log.TextFormatter{}})
	}
}

// Configure configures the logging singleton for operation inside a remote TTY (like SSH). In this
// mode an empty LogFile is not accepted and syslog is used as a fallback when LogFile could not be
// opened for writing.
func Configure(cfg *config.Config) {
	logFile, err := os.OpenFile(cfg.LogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		progName, _ := os.Executable()
		syslogLogger, syslogLoggerErr := syslog.NewLogger(syslog.LOG_ERR|syslog.LOG_USER, 0)
		if syslogLoggerErr == nil {
			msg := fmt.Sprintf("%s: Unable to configure logging: %v\n", progName, err.Error())
			syslogLogger.Print(msg)
		} else {
			msg := fmt.Sprintf("%s: Unable to configure logging: %v, %v\n", progName, err.Error(), syslogLoggerErr.Error())
			fmt.Fprintf(os.Stderr, msg)
		}

		// Discard logs since a log file was specified but couldn't be opened
		log.SetOutput(ioutil.Discard)
	}

	log.SetOutput(logFile)

	configureLogFormat(cfg)
}

// ConfigureStandalone configures the logging singleton for standalone operation. In this mode an
// empty LogFile is treated as logging to standard output and standard output is used as a fallback
// when LogFile could not be opened for writing.
func ConfigureStandalone(cfg *config.Config) {
	if cfg.LogFile == "" {
		return
	}

	logFile, err := os.OpenFile(cfg.LogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		log.Printf("Unable to configure logging, falling back to stdout: %v", err)
		return
	}
	log.SetOutput(logFile)

	configureLogFormat(cfg)
}
