// Copyright (c) 2013 CloudFlare, Inc.

package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"strings"

	"github.com/cloudflare/redoctober/config"
	"github.com/cloudflare/redoctober/core"
	"github.com/cloudflare/redoctober/report"
	"github.com/cloudflare/redoctober/server"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// initPrometheus starts a goroutine with a Prometheus listener that
// listens on localhost:metricsPort. If the Prometheus handler can't
// be started, a log.Fatal call is made.
func initPrometheus() {
	srv := &http.Server{
		Addr:    net.JoinHostPort(cfg.Metrics.Host, cfg.Metrics.Port),
		Handler: promhttp.Handler(),
	}

	log.Printf("metrics.init start: addr=%s", srv.Addr)
	go func() {
		err := srv.ListenAndServe()
		report.Check(err, nil)
		log.Fatal(err.Error())
	}()
}

const usage = `Usage:

	redoctober -static <path> -vaultpath <path> -addr <addr> -certs <path1>[,<path2>,...] -keys <path1>[,<path2>,...] [-ca <path>]

single-cert example:
redoctober -vaultpath diskrecord.json -addr localhost:8080 -certs cert.pem -keys cert.key
multi-cert example:
redoctober -vaultpath diskrecord.json -addr localhost:8080 -certs cert1.pem,cert2.pem -keys cert1.key,cert2.key
`

var (
	cfg, cli  *config.Config
	confFile  string
	vaultPath string
)

const (
	defaultAddr        = "localhost:8080"
	defaultMetricsHost = "localhost"
	defaultMetricsPort = "8081"
)

func registerCli() {
	// cli contains the configuration set by the command line
	// options, and cfg is the actual Red October config.
	cli = config.New()
	cfg = config.New()

	// customized the default index html with auto generated content
	server.DefaultIndexHTML = indexHTML

	cli.Server.Addr = defaultAddr
	cli.Metrics.Host = defaultMetricsHost
	cli.Metrics.Port = defaultMetricsPort

	flag.Usage = func() {
		fmt.Fprint(os.Stderr, "main usage dump\n")
		fmt.Fprint(os.Stderr, usage)
		flag.PrintDefaults()
		os.Exit(2)
	}

	flag.StringVar(&confFile, "f", "", "path to config file")
	flag.StringVar(&cli.Server.Addr, "addr", cli.Server.Addr,
		"Server and port separated by :")
	flag.StringVar(&cli.Server.CAPath, "ca", cli.Server.CAPath,
		"Path of TLS CA for client authentication (optional)")
	flag.StringVar(&cli.Server.CertPaths, "certs", cli.Server.CertPaths,
		"Path(s) of TLS certificate in PEM format, comma-separated")
	flag.StringVar(&cli.HipChat.Host, "hchost", cli.HipChat.Host,
		"Hipchat Url Base (ex: hipchat.com)")
	flag.StringVar(&cli.HipChat.APIKey, "hckey", cli.HipChat.APIKey,
		"Hipchat API Key")
	flag.StringVar(&cli.HipChat.Room, "hcroom", cli.HipChat.Room,
		"Hipchat Room ID")
	flag.StringVar(&cli.Server.KeyPaths, "keys", cli.Server.KeyPaths,
		"Comma-separated list of PEM-encoded TLS private keys in the same order as certs")
	flag.StringVar(&cli.Metrics.Host, "metrics-host", cli.Metrics.Host,
		"The `host` the metrics endpoint should listen on.")
	flag.StringVar(&cli.Metrics.Port, "metrics-port", cli.Metrics.Port,
		"The `port` the metrics endpoint should listen on.")
	flag.StringVar(&cli.UI.Root, "rohost", cli.UI.Root, "RedOctober URL Base (ex: localhost:8080)")
	flag.StringVar(&cli.UI.Static, "static", cli.UI.Static,
		"Path to override built-in index.html")
	flag.BoolVar(&cli.Server.Systemd, "systemdfds", cli.Server.Systemd,
		"Use systemd socket activation to listen on a file. Useful for binding privileged sockets.")
	flag.StringVar(&vaultPath, "vaultpath", "diskrecord.json", "Path to the the disk vault")

	flag.Parse()
}

//go:generate go run generate.go

func main() {
	registerCli()
	var err error
	if confFile != "" {
		cfg, err = config.Load(confFile)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		cfg = cli
	}

	report.Init(cfg)

	if vaultPath == "" || !cfg.Valid() {
		if !cfg.Valid() {
			fmt.Fprintf(os.Stderr, "Invalid config.\n")
		}
		fmt.Fprint(os.Stderr, usage)
		flag.PrintDefaults()
		os.Exit(2)
	}

	if err := core.Init(vaultPath, cfg); err != nil {
		report.Check(err, nil)
		log.Fatal(err)
	}

	initPrometheus()
	cpaths := strings.Split(cfg.Server.CertPaths, ",")
	kpaths := strings.Split(cfg.Server.KeyPaths, ",")
	s, l, err := server.NewServer(cfg.UI.Static, cfg.Server.Addr, cfg.Server.CAPath,
		cpaths, kpaths, cfg.Server.Systemd)
	if err != nil {
		report.Check(err, nil)
		log.Fatalf("Error starting redoctober server: %s\n", err)
	}

	log.Printf("http.serve start: addr=%s", cfg.Server.Addr)
	report.Recover(func() {
		err := s.Serve(l)
		report.Check(err, nil)
		log.Fatal(err.Error())
	})
}
