/*
Copyright © 2025 cavaliba.com
*/
package cmd

import (
	"errors"
	"fmt"
	"os"
	"strings"

	"cavpoller/poller"

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

var (
	// Used for flags.
	cfgFile        string
	url            string
	client_id      string
	client_secret  string
	timeout        int
	verbose        bool
	ssl_skipverify bool
	parallel       int
	single         bool
)

var cavpoller_version = "1.0.0"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "cavpoller",
	Short: "Standalone command to poll applications for Cavaliba Status App",
	Long:  `Standalone command to poll applications for Cavaliba Status App`,
	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
		return initializeConfig(cmd)
	},
	// Uncomment the following line if your bare application
	// has an action associated with it:
	Run: func(cmd *cobra.Command, args []string) {
		var err error
		if single {
			err = poller.RunSequence()
		} else {
			err = poller.RunParallel()
		}
		if err != nil {
			fmt.Printf("Error: %v\n", err)
			os.Exit(1)
		}
	},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	err := rootCmd.Execute()
	if err != nil {
		os.Exit(1)
	}
}

func init() {
	// Here you will define your flags and configuration settings.
	// Cobra supports persistent flags, which, if defined here,
	// will be global for your application.

	// 'P' functions accept a single letter shortcut

	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
	rootCmd.PersistentFlags().StringVar(&cfgFile, "conf", "", "config file (default is $HOME/.cavaliba/default.yaml)")

	rootCmd.PersistentFlags().StringVar(&url, "url", "http://localhost:8000/", "Cavaliba Root URL")
	rootCmd.PersistentFlags().StringVar(&client_id, "client_id", "", "API Client ID")
	rootCmd.PersistentFlags().StringVar(&client_secret, "client_secret", "", "API Client Secret")
	rootCmd.PersistentFlags().IntVar(&timeout, "timeout", 20000, "API timeout (msec)")
	rootCmd.PersistentFlags().BoolVar(&ssl_skipverify, "ssl_skipverify", false, "skip TLS/SSL cert verification")
	rootCmd.PersistentFlags().IntVar(&parallel, "parallel", 4, "Number of concurrent workers for parallel mode")
	rootCmd.PersistentFlags().BoolVar(&single, "single", false, "Use sequential mode instead of parallel")

	// Cobra also supports local flags, which will only run
	// when this action is called directly.
	//rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

func initializeConfig(cmd *cobra.Command) error {
	// 1. Set up Viper to use environment variables.
	viper.SetEnvPrefix("CAVALIBA_CAVPOLLER")
	// Allow for nested keys in environment variables (e.g. `MYAPP_DATABASE_HOST`)
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "*", "-", "*"))
	viper.AutomaticEnv()

	// 2. Handle the configuration file.
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Search for a config file in default locations.
		home, err := os.UserHomeDir()
		// Only panic if we can't get the home directory.
		cobra.CheckErr(err)

		// Search for a config file with the name "config" (without extension).
		viper.AddConfigPath(".")
		viper.AddConfigPath(home + "/.cavaliba")
		viper.SetConfigName("default")
		viper.SetConfigType("yaml")
	}

	// 3. Read the configuration file.
	// If a config file is found, read it in. We use a robust error check
	// to ignore "file not found" errors, but panic on any other error.
	if err := viper.ReadInConfig(); err != nil {
		// It's okay if the config file doesn't exist.
		var configFileNotFoundError viper.ConfigFileNotFoundError
		if !errors.As(err, &configFileNotFoundError) {
			return err
		}
	}

	// 4. Bind Cobra flags to Viper.
	// This is the magic that makes the flag values available through Viper.
	// It binds the full flag set of the command passed in.
	err := viper.BindPFlags(cmd.Flags())
	if err != nil {
		return err
	}

	// Set defaults for configuration values
	viper.SetDefault("parallel", 4)
	viper.SetDefault("single", false)

	if verbose {
		fmt.Println("Configuration")
		fmt.Println("=============")
		fmt.Println("File:            ", viper.ConfigFileUsed())
		fmt.Println("url:             ", viper.GetString("url"))
		fmt.Println("timeout:         ", viper.GetInt("timeout"))
		fmt.Println("client_id:       ", viper.GetString("client_id"))
		fmt.Println("ssl_skipverify:  ", viper.GetBool("ssl_skipverify"))
		fmt.Println("parallel:        ", viper.GetInt("parallel"))
		fmt.Println("single:          ", viper.GetBool("single"))
		fmt.Println("")
	}

	return nil
}
