package config

import (
	"context"

	"github.com/creasty/defaults"
	"github.com/go-playground/validator/v10"
	"github.com/pingcap/errors"
	"github.com/spf13/viper"
	ov "github.com/tidbcloud/config-service/oneconfig/viper"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/aipower/consts"
)

// @Time    : 2022/11/14 4:56 PM
// @Author  : lvhongmeng

const (
	SetSQLModeEnable                   = "set-sql-mode-enable"
	GetInformationSchemaEnable         = "get-information-schema-enable"
	SetSQLModeEnableChannels           = "set-sql-mode-enable-channels"
	GetInformationSchemaEnableChannels = "get-information-schema-enable-channels"
	RateLimitWhitelist                 = "rate-limit-whitelist"
	TimeOutForChat2Query               = "time-out-for-chat2query"
)

var validate = validator.New()

func Global() *CentralConfig {
	return globalConf
}

// InitConfig parse the central configuration file and init the global config var
func InitConfig(ctx context.Context, path string) error {
	viper.SetConfigFile(path)
	viper.AutomaticEnv() // for M2M env variables
	err := viper.ReadInConfig()
	if err != nil {
		tilog.Error(ctx, "failed to read in config", zap.Error(err))
		return err
	}

	// get secret from env
	viper.MustBindEnv("mysql.password", "MYSQL_PASSWORD")
	viper.MustBindEnv("sentry.dsn", "SENTRY_DSN")
	viper.MustBindEnv("messagesvc.openaiApiKey", "MESSAGESVC_OPENAIAPIKEY")

	err = NormalizeConfig()
	if err != nil {
		tilog.Error(ctx, "failed to normalize config", zap.Error(err))
		return err
	}
	InitRemoteDefault()
	// The remote config is not in CentralConfig
	err = LoadRemote()
	if err != nil {
		tilog.Error(ctx, "failed to load remote", zap.Error(err))
		return err
	}

	return nil
}

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

	err := NormalizeConfigDefaults(config)
	if err != nil {
		return err
	}
	err = NormalizeConfigSetup(config)
	if err != nil {
		return err
	}
	err = NormalizeConfigValidate(config)
	if err != nil {
		return err
	}
	globalConf = config
	return nil
}

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

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

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

func InitRemoteDefault() {
	viper.SetDefault(SetSQLModeEnable, true)
	viper.SetDefault(GetInformationSchemaEnable, true)
	viper.SetDefault(SetSQLModeEnableChannels, []string{consts.AIModelChat2SQL})
	viper.SetDefault(GetInformationSchemaEnableChannels, []string{consts.AIModelAll})
	viper.SetDefault(RateLimitWhitelist, map[string]int{})
	viper.SetDefault(TimeOutForChat2Query, 60)
}

func LoadRemote() error {
	remote := "oneconfig-aipower/config.yaml"
	err := viper.AddRemoteProvider(ov.Provider, ov.Endpoint, remote)
	if err != nil {
		return errors.Annotate(err, "failed to add remote provider")
	}
	err = viper.WatchRemoteConfig()
	if err != nil {
		return errors.Annotate(err, "failed to watch remote config")
	}
	err = viper.GetViper().WatchRemoteConfigOnChannel()
	if err != nil {
		return errors.Annotate(err, "failed to watch remote config on channel")
	}
	return nil
}
