// Copyright (C) 2023 Marius Schellenberger

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"syscall"
	"time"

	"git.giftfish.de/ston1th/godrop/v2"
	"git.giftfish.de/ston1th/keyctl/pkg/api"
	"git.giftfish.de/ston1th/keyctl/pkg/cli"
	"git.giftfish.de/ston1th/keyctl/pkg/db"
	"github.com/go-logr/logr"
	"k8s.io/klog/v2"
	"k8s.io/klog/v2/klogr"
)

var (
	version string

	listen string
	socket string
	path   string

	log logr.Logger
)

func usage() {
	fmt.Printf(`keyctl %s:
  supported subcommands:
    keyctl server  - starts a keyctl server
    keyctl get     - retrieve a key from the server
    keyctl new     - generates a new key (admin)
    keyctl ls      - list configured keys (admin)
    keyctl req     - list current key requests (admin)
    keyctl approve - approve a key request (admin)
    keyctl reject  - reject a key request (admin)
    keyctl del     - delete a key (admin)
    keyctl help    - show this usage
`, version)
	os.Exit(1)
}

func main() {
	if len(os.Args) < 2 {
		usage()
	}
	arg := os.Args[1]
	switch arg {
	case "new", "get", "ls", "req", "approve", "reject", "del":
		cli.Run(arg) // does not return
	case "server":
		server() // does not return
	default:
		usage() // does not return
	}
}

func server() {
	fs := flag.NewFlagSet("", flag.ExitOnError)
	klog.InitFlags(fs)
	fs.StringVar(&listen, "listen", ":7070", "listen ip:port")
	fs.StringVar(&socket, "socket", "keyctl.sock", "listen unix domain socket")
	fs.StringVar(&path, "path", "/var/keyctl", "db storage dir")
	fs.Parse(os.Args[2:])

	log = klogr.New().WithName("main")
	log.Info("starting keyctl", "version", version)
	err := godrop.PledgePromises("stdio rpath wpath cpath inet fattr flock unix unveil")
	if err != nil {
		klog.Fatalf("init failed: %s", err)
	}

	dbstore, err := db.New(klogr.New().WithName("db"), path)
	if err != nil {
		klog.Fatalf("init failed: %s", err)
	}

	srv, err := api.NewServer(klogr.New().WithName("srv"), listen, filepath.Join(path, socket), dbstore)
	if err != nil {
		klog.Fatalf("init failed: %s", err)
	}

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
	<-sigs
	log.Info("keyctl shutdown initiated")
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	srv.Shutdown(ctx)
	cancel()
	err = dbstore.Close()
	if err != nil {
		log.Error(err, "error closing db")
	}
	log.Info("keyctl shutdown completed")
	os.Exit(0)
}
