// Package main provides an STFE server binary
package main

import (
	"flag"
	"fmt"
	"time"

	"crypto/x509"
	"io/ioutil"
	"net/http"

	"github.com/golang/glog"
	"github.com/google/trillian"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/system-transparency/stfe"
	"github.com/system-transparency/stfe/x509util"
	"google.golang.org/grpc"
)

var (
	httpEndpoint = flag.String("http_endpoint", "localhost:6965", "host:port specification of where stfe serves clients")
	rpcBackend   = flag.String("log_rpc_server", "localhost:6962", "host:port specification of where Trillian serves clients")
	prefix       = flag.String("prefix", "st/v1", "a prefix that proceeds each endpoint path")
	trillianID   = flag.Int64("trillian_id", 5991359069696313945, "log identifier in the Trillian database")
	rpcDeadline  = flag.Duration("rpc_deadline", time.Second*10, "deadline for backend RPC requests")
	anchorPath   = flag.String("anchor_path", "../x509util/testdata/anchors.pem", "path to a file containing PEM-encoded X.509 root certificates")
	keyPath      = flag.String("key_path", "../x509util/testdata/log.key", "path to a PEM-encoded ed25519 signing key")
	maxRange     = flag.Int64("max_range", 2, "maximum number of entries that can be retrived in a single request")
	maxChain     = flag.Int64("max_chain", 3, "maximum number of certificates in a chain, including the trust anchor")
)

func main() {
	flag.Parse()

	glog.Info("Dialling Trillian gRPC log server")
	dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithBlock(), grpc.WithTimeout(*rpcDeadline)}
	conn, err := grpc.Dial(*rpcBackend, dialOpts...)
	if err != nil {
		glog.Fatal(err)
	}
	client := trillian.NewTrillianLogClient(conn)

	glog.Info("Creating HTTP request multiplexer")
	mux := http.NewServeMux()
	http.Handle("/", mux)

	glog.Info("Adding prometheus handler on path: /metrics")
	http.Handle("/metrics", promhttp.Handler())

	glog.Infof("Loading trust anchors from file: %s", *anchorPath)
	anchors, err := loadCertificates(*anchorPath)
	if err != nil {
		glog.Fatalf("no trust anchors: %v", err)
	}

	glog.Infof("Loading Ed25519 signing key from file: %s", *keyPath)
	pem, err := ioutil.ReadFile(*keyPath)
	if err != nil {
		glog.Fatalf("no signing key: %v", err)
	}
	signer, err := x509util.NewEd25519PrivateKey(pem)
	if err != nil {
		glog.Fatalf("no signing key: %v", err)
	}

	lp, err := stfe.NewLogParameters(*trillianID, *prefix, anchors, signer, *maxRange, *maxChain)
	if err != nil {
		glog.Fatalf("failed setting up log parameters: %v", err)
	}

	i := stfe.NewInstance(lp, client, *rpcDeadline)
	for _, handler := range i.Handlers() {
		glog.Infof("adding handler: %s", handler.Path())
		mux.Handle(handler.Path(), handler)
	}
	glog.Infof("Configured: %s", i)

	glog.Infof("Serving on %v/%v", *httpEndpoint, *prefix)
	srv := http.Server{Addr: *httpEndpoint}
	err = srv.ListenAndServe()
	if err != http.ErrServerClosed {
		glog.Warningf("Server exited: %v", err)
	}

	glog.Flush()
}

// loadCertificates loads a non-empty list of PEM-encoded certificates from file
func loadCertificates(path string) ([]*x509.Certificate, error) {
	pem, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("failed reading %s: %v", path, err)
	}
	anchors, err := x509util.NewCertificateList(pem)
	if err != nil {
		return nil, fmt.Errorf("failed parsing: %v", err)
	}
	if len(anchors) == 0 {
		return nil, fmt.Errorf("no trust anchors")
	}
	return anchors, nil
}