package main

import (
	"flag"
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"path"

	"github.com/pingcap/log"
	"github.com/pingcap/tidb-operator/pkg/client/clientset/versioned"
	"github.com/tidbcloud/clusterproxy/auth"
	"github.com/tidbcloud/clusterproxy/metrics"
	"github.com/tidbcloud/clusterproxy/resolve"
	"github.com/tidbcloud/clusterproxy/route"
	"github.com/tidbcloud/clusterproxy/route/router"
	_ "github.com/tidbcloud/clusterproxy/service"
	"go.uber.org/zap"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

var (
	addr       = flag.String("addr", "0.0.0.0", "Proxy listening address")
	port       = flag.Int("port", 80, "Proxy listening port")
	pprofPort  = flag.Int("pprof-port", 6060, "Pprof listening port")
	kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	tlsDir     = flag.String("tls-dir", "", "The directory for the TLS key and cert pair")
	tlsCert    = flag.String("tls-cert", "tls.crt", "The relative path of the TLS cert")
	tlsKey     = flag.String("tls-key", "tls.key", "The relative path of the TLS key")
	devMode    = flag.Bool("dev", false, "Enable the dev mode.")
	qps        = flag.Int("qps", 50, "Max QPS of K8S client")
	brust      = flag.Int("burst", 100, "Max Burst QPS of K8S client")
)

func main() {
	flag.Parse()

	// Check and set auth.Auth0ClientSecret for backward compatability.
	if *devMode || *auth.Auth0ClientSecret != "" {
		secret := "secret"
		auth.Auth0ClientSecret = &secret
		log.SetLevel(zap.DebugLevel)
		log.Info("Dev mode is on.")
	}

	// use the current context in kubeconfig
	restConfig, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

	restConfig.QPS = float32(*qps)
	restConfig.Burst = *brust

	cli, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		log.Fatal("Unable to create a Kubernetes client set from the REST config.", zap.Reflect("config", restConfig))
	}
	opcli, err := versioned.NewForConfig(restConfig)
	if err != nil {
		log.Fatal("Unable to create a TiDB cluster client set from the REST config.", zap.Reflect("config", restConfig))
	}

	stop := make(chan struct{})
	factory := informers.NewSharedInformerFactory(cli, 0)
	svcLister := factory.Core().V1().Services().Lister()
	factory.Start(stop)
	factory.WaitForCacheSync(stop)

	r := router.New(&route.Context{Cli: cli, SvcLister: svcLister}, metrics.Middleware, auth.Middleware)
	byID, err := resolve.NewByID(cli, svcLister)
	if err != nil {
		log.Fatal("Unable to create the resolver by id", zap.Error(err))
	}
	r.RegisterResolver(byID)

	byNamespacedName, err := resolve.NewByNamespacedName(cli, opcli)
	if err != nil {
		log.Fatal("Unable to create the resolver by namedspaced name", zap.Error(err))
	}
	r.RegisterResolver(byNamespacedName)

	mux := http.NewServeMux()
	if err := r.Bind(mux); err != nil {
		log.Fatal(err.Error())
	}

	mux.HandleFunc("/readyz", ready)

	go func() {
		if err := http.ListenAndServe(fmt.Sprintf("%s:%d", *addr, *pprofPort), nil); err != nil {
			log.Fatal(err.Error())
		}
	}()

	go metrics.StartMetricServer()
	addr := fmt.Sprintf("%s:%d", *addr, *port)
	log.Info("Serve proxy", zap.String("address", addr))
	srv := http.Server{
		Addr:    addr,
		Handler: mux,
	}
	if *tlsDir == "" {
		log.Info("Missing TLS directory, starting HTTP server")
		if err = srv.ListenAndServe(); err != nil {
			log.Fatal("Server ended unexpectedly.", zap.Error(err))
		}
	} else {
		log.Info("Starting HTTPS server")
		if err = srv.ListenAndServeTLS(path.Join(*tlsDir, *tlsCert), path.Join(*tlsDir, *tlsKey)); err != nil {
			log.Fatal("Server ended unexpectedly.", zap.Error(err))
		}
	}

}

func ready(_ http.ResponseWriter, _ *http.Request) {
}
