package cmd

import (
	"fmt"
	"os"

	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/ginserver"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
	"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var (
	config  = &handler.Config{}
	cfgFile string
)

const (
	parameterConfigurationFile = "config"
	parameterLogLevel          = "loglevel"
	parameterMock              = "mock"
	parameterLogFormat         = "logformat"
	parameterDBConnectionURI   = "dbconnectionuri"
	parameterPort              = "port"
)

var (
	defaultLogLevel        = logrus.WarnLevel.String()
	defaultLogFormat       = utils.LogFormatText
	defaultDBConnectionURI = ""
	defaultPort            = 8080
)

var rootCmd = &cobra.Command{
	Use:   "budget",
	Short: "budget",
	Run: func(cmd *cobra.Command, args []string) {
		utils.InitLogger(config.LogLevel, config.LogFormat)
		logrus.
			WithField(parameterConfigurationFile, cfgFile).
			WithField(parameterMock, config.Mock).
			WithField(parameterLogLevel, config.LogLevel).
			WithField(parameterLogFormat, config.LogFormat).
			WithField(parameterPort, config.Port).
			WithField(parameterDBConnectionURI, config.DBConnectionURI).
			Warn("Configuration")
		router := ginserver.NewRouter(config)
		err := router.Run(fmt.Sprintf(":%d", config.Port))
		if err != nil {
			return
		}
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	rootCmd.PersistentFlags().StringVar(&cfgFile, parameterConfigurationFile, "", "Config file. All flags given in command line will override the values from this file.")

	rootCmd.Flags().String(parameterLogLevel, defaultLogLevel, "Use this flag to set the logging level")
	err := viper.BindPFlag(parameterLogLevel, rootCmd.Flags().Lookup(parameterLogLevel))
	if err != nil {
		return
	}

	rootCmd.Flags().String(parameterLogFormat, defaultLogFormat, "Use this flag to set the logging format")
	err = viper.BindPFlag(parameterLogFormat, rootCmd.Flags().Lookup(parameterLogFormat))
	if err != nil {
		return
	}

	rootCmd.Flags().String(parameterDBConnectionURI, defaultDBConnectionURI, "Use this flag to set the db connection URI")
	err = viper.BindPFlag(parameterDBConnectionURI, rootCmd.Flags().Lookup(parameterDBConnectionURI))
	if err != nil {
		return
	}

	rootCmd.Flags().Int(parameterPort, defaultPort, "Use this flag to set the listening port of the api")
	err = viper.BindPFlag(parameterPort, rootCmd.Flags().Lookup(parameterPort))
	if err != nil {
		return
	}

	rootCmd.Flags().Bool(parameterMock, false, "Use this flag to enable the mock mode")
	err = viper.BindPFlag(parameterMock, rootCmd.Flags().Lookup(parameterMock))
	if err != nil {
		return
	}
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}

	config.LogLevel = viper.GetString(parameterLogLevel)
	config.LogFormat = viper.GetString(parameterLogFormat)
	config.Mock = viper.GetBool(parameterMock)
	config.DBConnectionURI = viper.GetString(parameterDBConnectionURI)
	config.Port = viper.GetInt(parameterPort)
}
