package config

import (
	"os"

	"github.com/creasty/defaults"
	"github.com/go-playground/validator/v10"
	"github.com/juju/errors"
	"github.com/pingcap/log"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)

var (
	globalConf = &Config{}
	validate   = validator.New()
)

// InitConfig parse the central configuration file and init the global config var
func InitConfig(path string) error {
	viper.SetConfigFile(path)
	viper.AutomaticEnv() // for M2M env variables
	if err := viper.ReadInConfig(); err != nil {
		return err
	}

	return NormalizeConfig()
}

func InitMockConfig() {
	os.Remove("/tmp/load_data_task.parquet")
	os.RemoveAll("/Users/clark/coding/serverless-metering/tmp/rcu")
	config := &Config{
		Query: QueryEngineConfig{Directory: "/tmp"},
		Store: StoreConfig{
			Local: LocalConfig{
				Filename:   "/tmp/ut.db",
				WriteBatch: 10000,
			},
			Rcu: RcuStoreConfig{
				Directory: "/Users/clark/coding/serverless-metering/tmp/rcu",
			},
		},
	}
	_ = NormalizeConfigDefaults(config)
	globalConf = config
}

func NormalizeConfigDefaults(config *Config) error {
	return defaults.Set(config)
}

func NormalizeConfigSetup(conf *Config) error {
	err := viper.Unmarshal(conf)
	if err != nil {
		return errors.Annotate(err, "failed unmarshal from viper")
	}

	return nil
}

func NormalizeConfigValidate(config *Config) error {
	// validate the values
	if err := validate.Struct(config); err != nil {
		return errors.Annotate(err, "failed to validate the config values")
	}
	return nil
}

func NormalizeConfig() error {
	config := &Config{}

	if err := NormalizeConfigDefaults(config); err != nil {
		return err
	}

	if err := NormalizeConfigSetup(config); err != nil {
		return err
	}

	if err := NormalizeConfigValidate(config); err != nil {
		return err
	}

	globalConf = config
	log.Info("normalize config", zap.Any("config", config))
	return nil
}

func Global() *Config {
	return globalConf
}
