package config

import (
	"flag"
	"log/slog"
	"os"
	"path/filepath"
	"strings"

	"github.com/fsnotify/fsnotify"
	"github.com/pingcap/dbaas/billing2/billing_errors"
	"github.com/pingcap/dbaas/billing2/utils/log"
	slogzap "github.com/samber/slog-zap/v2"

	"github.com/spf13/viper"
	ov "github.com/tidbcloud/config-service/oneconfig/viper"
	"go.uber.org/zap"
)

var (
	configPath        = flag.String("conf", "/etc/billing/", "billing system configuration file")
	secretsConfigPath = flag.String("secret", "/etc/billing_secrets", "secrets configuration file")
)

func LoadRemote() error {
	remote := "oneconfig-billing/config.yaml"
	log.Info("Read From Remote", zap.String("remote", remote))
	err := Get().AddRemoteProvider(ov.Provider, ov.Endpoint, remote)
	Get().SetConfigType("yaml")
	if err != nil {
		return billing_errors.NewSystemError(err)
	}

	err = Get().WatchRemoteConfig()
	if err != nil {
		return billing_errors.NewSystemError(err)
	}

	err = Get().WatchRemoteConfigOnChannel()
	if err != nil {
		return billing_errors.NewSystemError(err)
	}

	log.Info("Remote configuration loaded", zap.String("remote", remote))
	return nil
}

var viperInstance = viper.NewWithOptions(viper.WithLogger(slog.New(slogzap.Option{Level: slog.LevelInfo, Logger: log.L()}.NewZapHandler())))
var secureViperInstance = viper.NewWithOptions(viper.WithLogger(slog.New(slogzap.Option{Level: slog.LevelInfo, Logger: log.L()}.NewZapHandler())))

func Get() *viper.Viper {
	return viperInstance
}

func Reset() {
	viperInstance = viper.NewWithOptions(viper.WithLogger(slog.New(slogzap.Option{Level: slog.LevelInfo, Logger: log.L()}.NewZapHandler())))
}

// Init init the viper
func Init() error {

	err := InitLocal()
	if err != nil {
		return err
	}

	if Get().GetBool(EnableRemoteConfig) {
		err = LoadRemote()
		if err != nil {
			log.Warn("load remote config failed, just ignore it in persnonal project", zap.Error(err))
		}

		Get().OnConfigChange(func(e fsnotify.Event) {
			log.Info("Config file changed:", zap.String("path", e.Name))
		})
	}
	return nil
}

func InitSecrets() error {
	files, err := os.ReadDir(*secretsConfigPath)
	if err != nil {
		if os.IsNotExist(err) {
			log.Info("secrets config dir does not exist", zap.Stringp("path", secretsConfigPath))
			return nil
		}
		log.Fatal("Fatal error reading secret directory", zap.Stringp("path", secretsConfigPath), zap.Error(err))
		return err
	}
	for _, file := range files {
		if file.IsDir() || strings.HasPrefix(file.Name(), ".") {
			continue
		}

		key := file.Name()
		filePath := filepath.Join(*secretsConfigPath, key)

		valueBytes, err := os.ReadFile(filePath)
		if err != nil {
			log.Fatal("Error reading secret file", zap.String("key", key), zap.Error(err))
			continue
		}

		Get().Set(key, strings.TrimSpace(string(valueBytes)))
	}
	return nil
}

func InitLocal() error {
	flag.Parse()

	SetDefault()
	Get().AddConfigPath(*configPath)
	Get().AutomaticEnv()
	err := Get().ReadInConfig()
	if err != nil {
		return billing_errors.NewSystemError(err)
	}
	Get().WatchConfig()
	if err := InitSecrets(); err != nil {
		return billing_errors.NewSystemError(err)
	}
	return nil
}
