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

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

	"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
	id             int
	key            string
	expand         bool
	format         string
	search         string
	page           int
	size           int
	schema         string
	file           string
	pipeline       string
	delimiter      string
	refs           string
	rev            int
	outfile        string
)

var cavctl_version = "1.8.0"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:               "cavctl",
	Short:             "standalone CLI command to manage remote Cavaliba systems",
	Long:              `cavctl is a CLI standalone command to manage remote Cavaliba systems`,
	CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
	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) {
		fmt.Printf("cavctl version: %s\n", cavctl_version)

	},
}

// 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

	// v1.1.0

	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/api/", "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", 7000, "API timeout (msec)")

	// v1.1.0
	rootCmd.PersistentFlags().BoolVar(&ssl_skipverify, "ssl_skipverify", false, "skip TLS/SSL cert verification")
	rootCmd.PersistentFlags().IntVar(&id, "id", 0, "ID filter")
	rootCmd.PersistentFlags().StringVar(&key, "key", "", "Key filter")
	rootCmd.PersistentFlags().BoolVar(&expand, "expand", false, "Expand result")
	rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "json", "Output format : json, yaml, txt")
	rootCmd.PersistentFlags().StringVar(&search, "search", "", "Search filter")
	rootCmd.PersistentFlags().IntVar(&page, "page", 0, "Page number")
	rootCmd.PersistentFlags().IntVar(&size, "size", 0, "Page size")
	rootCmd.PersistentFlags().StringVar(&schema, "schema", "", "Schema filter")
	rootCmd.PersistentFlags().StringVar(&file, "file", "", "File path for import/export operations")
	rootCmd.PersistentFlags().StringVar(&pipeline, "pipeline", "", "Pipeline name for data transformation")
	rootCmd.PersistentFlags().StringVar(&delimiter, "delimiter", ",", "CSV delimiter")
	rootCmd.PersistentFlags().StringVar(&refs, "refs", "", "Refs filter")
	rootCmd.PersistentFlags().IntVar(&rev, "rev", 0, "Number of revisions to include")
	rootCmd.PersistentFlags().StringVar(&outfile, "outfile", "", "Append results to file instead of stdout")

	// 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_CAVCTL")
	// 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).
		// NOTE: Don't search in current directory "." to avoid reading the binary itself
		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
	}

	if verbose {
		fmt.Println("Configuration")
		fmt.Println("-------------")
		fmt.Println("Configuration:  ", 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("")
	}

	return nil
}
