package config_test

import (
	"encoding/json"
	"os"
	"path"
	"testing"

	"git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common/config"
	configtest "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/config/test"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gopkg.in/yaml.v3"
)

func TestCreateReloadViper(t *testing.T) {
	type m = map[string]any

	dummyFileSize := 1 << 10

	configPath := t.TempDir()
	configFile := "000_a.yaml"

	configDirPath := path.Join(configPath, "conf.d")
	require.NoError(t, os.Mkdir(configDirPath, 0o700))

	configtest.PrepareConfigFiles(t, configPath, []configtest.ConfigFile{
		configtest.NewConfigFile(configFile, m{"a": "000"}, yaml.Marshal),
	})

	// Not valid configs, dummy files those appear lexicographically first.
	configtest.PrepareDummyFiles(t, configDirPath, []configtest.DummyFile{
		configtest.NewDummyFile("000_file_1", dummyFileSize),
		configtest.NewDummyFile("000_file_2", dummyFileSize),
		configtest.NewDummyFile("000_file_3", dummyFileSize),
	})

	configtest.PrepareConfigFiles(t, configDirPath, []configtest.ConfigFile{
		// Valid configs with invalid extensions those appear lexicographically first.
		configtest.NewConfigFile("001_a.yaml.un~", m{"a": "101"}, yaml.Marshal),
		configtest.NewConfigFile("001_b.yml~", m{"b": m{"a": "102", "b": "103"}}, yaml.Marshal),
		configtest.NewConfigFile("001_c.yaml.swp", m{"c": m{"a": "104", "b": "105"}}, yaml.Marshal),
		configtest.NewConfigFile("001_d.json.swp", m{"d": m{"a": "106", "b": "107"}}, json.Marshal),

		// Valid configs with valid extensions those should be loaded.
		configtest.NewConfigFile("010_a.yaml", m{"a": "1"}, yaml.Marshal),
		configtest.NewConfigFile("020_b.yml", m{"b": m{"a": "2", "b": "3"}}, yaml.Marshal),
		configtest.NewConfigFile("030_c.json", m{"c": m{"a": "4", "b": "5"}}, json.Marshal),

		// Valid configs with invalid extensions those appear lexicographically last.
		configtest.NewConfigFile("099_a.yaml.un~", m{"a": "201"}, yaml.Marshal),
		configtest.NewConfigFile("099_b.yml~", m{"b": m{"a": "202", "b": "203"}}, yaml.Marshal),
		configtest.NewConfigFile("099_c.yaml.swp", m{"c": m{"a": "204", "b": "205"}}, yaml.Marshal),
		configtest.NewConfigFile("099_c.json.swp", m{"d": m{"a": "206", "b": "207"}}, json.Marshal),
	})

	// Not valid configs, dummy files those appear lexicographically last.
	configtest.PrepareDummyFiles(t, configDirPath, []configtest.DummyFile{
		configtest.NewDummyFile("999_file_1", dummyFileSize),
		configtest.NewDummyFile("999_file_2", dummyFileSize),
		configtest.NewDummyFile("999_file_3", dummyFileSize),
	})

	finalConfig := m{"a": "1", "b": m{"a": "2", "b": "3"}, "c": m{"a": "4", "b": "5"}}

	var (
		v   *viper.Viper
		err error
	)

	t.Run("create config with config dir only", func(t *testing.T) {
		v, err = config.CreateViper(
			config.WithConfigDir(configDirPath),
		)
		require.NoError(t, err)
		assert.Equal(t, finalConfig, v.AllSettings())
	})

	t.Run("reload config with config dir only", func(t *testing.T) {
		err = config.ReloadViper(
			config.WithViper(v),
			config.WithConfigDir(configDirPath),
		)
		require.NoError(t, err)
		assert.Equal(t, finalConfig, v.AllSettings())
	})

	t.Run("create config with both config and config dir", func(t *testing.T) {
		v, err = config.CreateViper(
			config.WithConfigFile(path.Join(configPath, configFile)),
			config.WithConfigDir(configDirPath),
		)
		require.NoError(t, err)
		assert.Equal(t, finalConfig, v.AllSettings())
	})

	t.Run("reload config with both config and config dir", func(t *testing.T) {
		err = config.ReloadViper(
			config.WithViper(v),
			config.WithConfigFile(path.Join(configPath, configFile)),
			config.WithConfigDir(configDirPath),
		)
		require.NoError(t, err)
		assert.Equal(t, finalConfig, v.AllSettings())
	})
}