package main

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/mmcloughlin/professor"
	log "github.com/sirupsen/logrus"
	flag "github.com/spf13/pflag"

	"git.mills.io/prologic/msgbus"
)

const (
	helpText = `
msgbus is a self-hosted pub/sub server for publishing
artbitrary messages onto queues (topics) and subscribing to them with
a client such as curl or in real-time with websockets (using msgbus).

Valid optinos:
`
)

var (
	version bool
	debug   bool

	bind    string
	logPath string

	bufferLength   int
	maxQueueSize   int
	maxPayloadSize int
)

func init() {
	baseProg := filepath.Base(os.Args[0])
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", baseProg)
		fmt.Fprint(os.Stderr, helpText)
		flag.PrintDefaults()
	}

	flag.BoolVarP(&debug, "debug", "d", false, "enable debug logging")
	flag.BoolVarP(&version, "version", "v", false, "display version information")

	// Basic options
	flag.StringVarP(
		&bind, "bind", "b", msgbus.DefaultBind,
		"[int]:<port> to bind to",
	)
	flag.StringVarP(
		&logPath, "log-path", "l", msgbus.DefaultLogPath,
		"path to write log files to (wal)",
	)
	flag.IntVarP(
		&bufferLength, "buffer-length", "B", msgbus.DefaultBufferLength,
		"set the buffer length for subscribers before messages are dropped",
	)
	flag.IntVarP(
		&maxQueueSize, "max-queue-size", "Q", msgbus.DefaultMaxQueueSize,
		"set the maximum queue size per topic",
	)
	flag.IntVarP(
		&maxPayloadSize, "max-payload-size", "P", msgbus.DefaultMaxPayloadSize,
		"set the maximum payload size per message",
	)
}

func flagNameFromEnvironmentName(s string) string {
	s = strings.ToLower(s)
	s = strings.Replace(s, "_", "-", -1)
	return s
}

func parseArgs() error {
	for _, v := range os.Environ() {
		vals := strings.SplitN(v, "=", 2)
		flagName := flagNameFromEnvironmentName(vals[0])
		fn := flag.CommandLine.Lookup(flagName)
		if fn == nil || fn.Changed {
			continue
		}
		if err := fn.Value.Set(vals[1]); err != nil {
			return err
		}
	}
	flag.Parse()
	return nil
}

func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Headers", "*")
		w.Header().Set("Access-Control-Allow-Headers", "*")
		w.Header().Set("Access-Control-Expose-Headers", "*")
		next.ServeHTTP(w, r)
	})
}

func main() {
	parseArgs()

	if debug {
		log.SetLevel(log.DebugLevel)
	} else {
		log.SetLevel(log.InfoLevel)
	}

	if version {
		fmt.Printf("msgbusd %s", msgbus.FullVersion())
		os.Exit(0)
	}

	if debug {
		go professor.Launch(":6060")
	}

	mb, err := msgbus.NewMessageBus(
		msgbus.WithLogPath(logPath),
		msgbus.WithBufferLength(bufferLength),
		msgbus.WithMaxQueueSize(maxQueueSize),
		msgbus.WithMaxPayloadSize(maxPayloadSize),
		msgbus.WithMetrics(true),
	)
	if err != nil {
		log.WithError(err).Fatal("error configuring message bus")
	}

	http.Handle("/", corsMiddleware(mb))
	http.Handle("/metrics", mb.Metrics().Handler())
	log.Infof("msgbusd %s listening on %s", msgbus.FullVersion(), bind)
	log.Fatal(http.ListenAndServe(bind, nil))
}
