package flags

import (
	"fmt"
	"strings"

	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// FormatNewError reports the impossibility of flag1 and flag2 usage simultaneously
func FormatNewError(flag1, flag2 string) error {
	return errors.Errorf("can't provide '%s' and '%s' flags simultaneously",
		flag1,
		flag2)
}

// ValidateQuerySelectionFlags reports the impossibility of include and exclude flags usage simultaneously
func ValidateQuerySelectionFlags() error {
	if len(GetMultiStrFlag(IncludeQueriesFlag)) > 0 && len(GetMultiStrFlag(ExcludeQueriesFlag)) > 0 {
		return FormatNewError(IncludeQueriesFlag, ExcludeQueriesFlag)
	}
	if len(GetMultiStrFlag(IncludeQueriesFlag)) > 0 && len(GetMultiStrFlag(ExcludeCategoriesFlag)) > 0 {
		return FormatNewError(IncludeQueriesFlag, ExcludeCategoriesFlag)
	}
	return nil
}

// ValidateTypeSelectionFlags reports the impossibility of include and exclude flags usage simultaneously
func ValidateTypeSelectionFlags() error {
	if len(GetMultiStrFlag(TypeFlag)) > 1 && len(GetMultiStrFlag(ExcludeTypeFlag)) > 1 {
		return FormatNewError(TypeFlag, ExcludeTypeFlag)
	}
	if GetMultiStrFlag(TypeFlag)[0] != "" && GetMultiStrFlag(ExcludeTypeFlag)[0] != "" {
		return FormatNewError(TypeFlag, ExcludeTypeFlag)
	}
	return nil
}

// BindFlags fill flags values with config file or environment variables data
func BindFlags(cmd *cobra.Command, v *viper.Viper) error {
	log.Debug().Msg("console.bindFlags()")
	settingsMap := v.AllSettings()
	cmd.Flags().VisitAll(func(f *pflag.Flag) {
		settingsMap[f.Name] = true
		if strings.Contains(f.Name, "-") {
			envVarSuffix := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
			variableName := fmt.Sprintf("%s_%s", "KICS", envVarSuffix)
			if err := v.BindEnv(f.Name, variableName); err != nil {
				log.Err(err).Msg("Failed to bind Viper flags")
			}
		}
		if !f.Changed && v.IsSet(f.Name) {
			val := v.Get(f.Name)
			setBoundFlags(f.Name, val, cmd)
		}
	})
	for key, val := range settingsMap {
		if val != true {
			return fmt.Errorf("unknown configuration key: '%s'\nShowing help for '%s' command", key, cmd.Name())
		}
	}
	return nil
}

func setBoundFlags(flagName string, val interface{}, cmd *cobra.Command) {
	switch t := val.(type) {
	case []interface{}:
		var paramSlice []string
		for _, param := range t {
			paramSlice = append(paramSlice, param.(string))
		}
		valStr := strings.Join(paramSlice, ",")
		if err := cmd.Flags().Set(flagName, valStr); err != nil {
			log.Err(err).Msg("Failed to set Viper flags")
		}
	default:
		if err := cmd.Flags().Set(flagName, fmt.Sprintf("%v", val)); err != nil {
			log.Err(err).Msg("Failed to set Viper flags")
		}
	}
}
