package main

import (
	"fmt"
	"net/http"
	"runtime"
	"time"

	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/ckotzbauer/libstandard"
	"github.com/ckotzbauer/vulnerability-operator/internal/vuln"
	"github.com/ckotzbauer/vulnerability-operator/internal/vuln/daemon"
)

var (
	// Version sets the current Operator version
	Version = "0.0.1"
	Commit  = "main"
	Date    = ""
	BuiltBy = ""
)

func newRootCmd() *cobra.Command {
	rootCmd := &cobra.Command{
		Use:   "vulnerability-operator",
		Short: "An operator for scanning SBOMs for vulnerabilities.",
		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
			vuln.OperatorConfig = &vuln.Config{}
			return libstandard.DefaultInitializer(vuln.OperatorConfig, cmd, "vulnerability-operator")
		},
		Run: func(cmd *cobra.Command, args []string) {
			printVersion()

			daemon.Start(vuln.OperatorConfig.Cron)

			fs := http.FileServer(http.Dir(vuln.OperatorConfig.ReportsDir))

			logrus.Info("Webserver is running at port 8080")
			http.HandleFunc("/health", health)
			http.Handle("/report/", http.StripPrefix("/report", fs))
			http.Handle("/metrics", promhttp.Handler())

			server := &http.Server{
				Addr:              ":8080",
				ReadHeaderTimeout: 3 * time.Second,
			}

			logrus.WithError(server.ListenAndServe()).Fatal("Starting webserver failed!")
		},
	}

	libstandard.AddConfigFlag(rootCmd)
	libstandard.AddVerbosityFlag(rootCmd)
	rootCmd.PersistentFlags().String(vuln.ConfigKeyCron, "@hourly", "Backround-Service interval (CRON)")
	rootCmd.PersistentFlags().StringSlice(vuln.ConfigKeySources, []string{"git"}, "Comma-delimited list of sources to gather SBOMs from (git, kubernetes).")
	rootCmd.PersistentFlags().StringSlice(vuln.ConfigKeyTargets, []string{"json"}, "Comma-delimited list of targets to sent vulnerability-data to (json, metrics, policyreport).")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGrypeConfigFile, "", "Path to grype-config-file to specify ignore-rules.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyFilterConfigFile, "", "Path to filter-config-file to specify ignore- and audit-rules.")
	rootCmd.PersistentFlags().Bool(vuln.ConfigKeyOnlyFixed, false, "Only report CVEs where a fix is available.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyMinSeverity, "medium", "Only report CVEs with a severity greater or equal (negligible, low, medium, high, critical).")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitWorkingTree, "/work", "Directory to place the git-repo.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitRepository, "", "Git-Repository-URL (HTTPS).")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitBranch, "main", "Git-Branch to checkout.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitPath, "", "Folder-Path inside the Git-Repository.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitAccessToken, "", "Git-Access-Token.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitUserName, "", "Git-Username.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitPassword, "", "Git-Password.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitHubAppId, "", "GitHub App ID (for authentication).")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyGitHubAppInstallationId, "", "GitHub App Installation ID (for authentication).")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyReportsDir, "/reports", "Directory to place the reports.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyPodLabelSelector, "", "Kubernetes Label-Selector for pods.")
	rootCmd.PersistentFlags().String(vuln.ConfigKeyNamespaceLabelSelector, "", "Kubernetes Label-Selector for namespaces.")

	return rootCmd
}

func printVersion() {
	logrus.Info(fmt.Sprintf("Version: %s", Version))
	logrus.Info(fmt.Sprintf("Commit: %s", Commit))
	logrus.Info(fmt.Sprintf("Buit at: %s", Date))
	logrus.Info(fmt.Sprintf("Buit by: %s", BuiltBy))
	logrus.Info(fmt.Sprintf("Go Version: %s", runtime.Version()))
}

func health(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(200)
	_, err := fmt.Fprint(w, "Running!")
	if err != nil {
		logrus.WithError(err).Error("Writing response failed for health check")
	}
}

func main() {
	rootCmd := newRootCmd()
	err := rootCmd.Execute()
	if err != nil {
		panic(err)
	}
}
