package main

import (
	"os"
	"time"

	"github.com/spf13/cobra"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/healthz"
	"sigs.k8s.io/controller-runtime/pkg/webhook"

	"github.com/spinningfactory/kloak/pkg/logging"
	webhookpkg "github.com/spinningfactory/kloak/pkg/webhook"
)

var webhookScheme = runtime.NewScheme()

func init() {
	utilruntime.Must(clientgoscheme.AddToScheme(webhookScheme))
}

var webhookCmd = &cobra.Command{
	Use:   "webhook",
	Short: "Run the Kloak mutating admission webhook",
	Long:  `Starts the mutating admission webhook that rewrites Secret volumes to use Kloak shadow secrets.`,
	Run:   runWebhook,
}

var (
	webhookProbeAddr string
	webhookCertDir   string
)

func init() {
	webhookCmd.Flags().StringVar(&webhookProbeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
	webhookCmd.Flags().StringVar(&webhookCertDir, "cert-dir", "/certs", "Directory containing TLS certs.")
}

func runWebhook(cmd *cobra.Command, args []string) {
	setupLog := logging.Setup().Named("setup")

	setupLog.Infow("Starting Kloak webhook")

	gracefulShutdown := 30 * time.Second
	mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
		Scheme:                  webhookScheme,
		HealthProbeBindAddress:  webhookProbeAddr,
		GracefulShutdownTimeout: &gracefulShutdown,
		WebhookServer: webhook.NewServer(webhook.Options{
			Port:    9443,
			CertDir: webhookCertDir,
		}),
	})
	if err != nil {
		setupLog.Errorw("unable to start manager", "error", err)
		_ = setupLog.Sync()
		os.Exit(1)
	}

	// TLS certs are mounted from the kloak-webhook-certs secret, generated by the controller at startup.

	hookServer := mgr.GetWebhookServer()
	hookServer.Register("/mutate-pods", &webhook.Admission{
		Handler: webhookpkg.NewHandler(mgr.GetClient(), setupLog),
	})
	hookServer.Register("/validate-secrets", &webhook.Admission{
		Handler: webhookpkg.NewSecretValidator(setupLog),
	})

	// Add health checks
	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
		setupLog.Errorw("unable to set up health check", "error", err)
		_ = setupLog.Sync()
		os.Exit(1)
	}
	if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
		setupLog.Errorw("unable to set up ready check", "error", err)
		_ = setupLog.Sync()
		os.Exit(1)
	}

	setupLog.Infow("starting webhook server")
	signalCtx := ctrl.SetupSignalHandler()
	startErr := mgr.Start(signalCtx)
	if signalCtx.Err() != nil {
		setupLog.Infow("shutdown signal received, draining")
	}
	if startErr != nil {
		setupLog.Errorw("problem running manager", "error", startErr)
		_ = setupLog.Sync()
		os.Exit(1)
	}
	flushCoverage()
	setupLog.Infow("webhook exited cleanly")
	_ = setupLog.Sync()
}
