package main

import (
	"fmt"
	"log/syslog"
	"math/rand"
	"os"
	"strings"
	"time"

	logrus_logstash "github.com/bshuster-repo/logrus-logstash-hook"
	"github.com/sirupsen/logrus"
	logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
	"github.com/spf13/cobra"
)

const (
	defaultConfigFile       = "/etc/necronomicon/necronomicon.conf"
	defaultServiceDirectory = "/etc/necronomicon/services.d"
	defaultLogDriver        = "stdout"
	syslogFacility          = syslog.LOG_LOCAL4
)

var (
	logDrivers = map[string]logInit{
		"stdout":          stdoutLogInit,
		"syslog":          syslogLogInit,
		"syslog-logstash": syslogLogstashLogInit,
	}
)

type logInit func() error

// Error is an implementation of the error interface using strings as its
// internal representation.
type Error string

// Error returns the string.
func (e Error) Error() string { return string(e) }

var (
	cmd = &cobra.Command{
		Use:               "necrod",
		Short:             "necrod is an agent daemon for Necronomicon, a configuration management service",
		Long:              `necrod is an agent daemon for Necronomicon, a configuration management service`,
		PersistentPreRunE: preRunMain,
	}
	log = logrus.New()
)

func init() {
	rand.Seed(time.Now().UnixNano())
	cmd.PersistentFlags().StringP("config", "c", defaultConfigFile, "config file")
	cmd.PersistentFlags().StringP("services-directory", "D", defaultServiceDirectory, "services directory")
	cmd.PersistentFlags().BoolP("debug", "d", false, "enable debug logging")
	cmd.PersistentFlags().StringP("log-driver", "l", defaultLogDriver, fmt.Sprintf("logging driver (value values: %s)", strings.Join(stringKeys(logDrivers), ", ")))
}

func stringKeys(m map[string]logInit) []string {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i++
	}
	return keys
}

func preRunMain(cmd *cobra.Command, args []string) error {
	debug, err := cmd.Flags().GetBool("debug")
	if err != nil {
		return fmt.Errorf("Could not parse debug flag: %v", err)
	}
	if debug {
		log.Level = logrus.DebugLevel
		log.Debugln("Enabling debug logging")
	}
	driver, err := cmd.Flags().GetString("log-driver")
	if err != nil {
		return fmt.Errorf("Could not parse log-driver flag: %v", err)
	}
	logInit, ok := logDrivers[driver]
	if !ok {
		return fmt.Errorf("Invalid log driver: %s", driver)
	}
	err = logInit()
	if err != nil {
		return fmt.Errorf("Could not initialize log driver: %v", err)
	}
	return nil
}

func stdoutLogInit() error {
	return nil
}

func syslogLogInit() error {
	log.Formatter = &logrus.TextFormatter{
		DisableTimestamp: true,
	}
	syslogHook, err := logrus_syslog.NewSyslogHook("", "", syslogFacility, "necrod")
	if err != nil {
		return fmt.Errorf("Could not initialize syslog hook: %v", err)
	}
	log.Hooks.Add(syslogHook)
	return nil
}

func syslogLogstashLogInit() error {
	log.Formatter = &logrus_logstash.LogstashFormatter{}
	// https://twitchtv.atlassian.net/wiki/display/SYS/Working+with+Logging for how to select facility
	return syslogLogInit()
}

func main() {
	if err := cmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
