package config

import (
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNormalizeDynamicConfigDefaults(t *testing.T) {
	config := &DynamicConfig{}
	err := normalizeDynamicConfigDefaults(config)
	require.NoError(t, err)
	// ConnLimit is now a value type, so it will have default values
	assert.Equal(t, int64(400), config.ConnLimit.FreeStarter)
	assert.Equal(t, int64(5000), config.ConnLimit.PaidStarter)
}

func TestNormalizeDynamicConfig(t *testing.T) {
	tests := []struct {
		name            string
		data            string
		wantFreeStarter int64
		wantPaidStarter int64
		wantErr         bool
	}{
		{
			name: "config with values overriding defaults",
			data: `conn_limit:
  free_starter: 100
  paid_starter: 3000`,
			wantFreeStarter: 100,
			wantPaidStarter: 3000,
			wantErr:         false,
		},
		{
			name: "config with partial values preserves defaults",
			data: `conn_limit:
  free_starter: 150`,
			wantFreeStarter: 150,
			wantPaidStarter: 5000, // default value preserved when key is missing
			wantErr:         false,
		},
		{
			name:            "empty config uses all defaults",
			data:            "",
			wantFreeStarter: 400,
			wantPaidStarter: 5000, // default value preserved when key is missing
			wantErr:         false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			config := &DynamicConfig{}
			err := normalizeDynamicConfig(config, tt.data)

			if tt.wantErr {
				require.Error(t, err)
				return
			}

			require.NoError(t, err)
			require.NotNil(t, config.ConnLimit)
			assert.Equal(t, tt.wantFreeStarter, config.ConnLimit.FreeStarter)
			assert.Equal(t, tt.wantPaidStarter, config.ConnLimit.PaidStarter)
		})
	}
}

func TestNewDynamicConfigWatcher(t *testing.T) {
	tests := []struct {
		name             string
		cfg              *DynamicConfigWatcherConfig
		wantConfigPath   string
		wantPollInterval time.Duration
	}{
		{
			name:             "empty config uses defaults",
			cfg:              &DynamicConfigWatcherConfig{},
			wantConfigPath:   defaultConfigPath,
			wantPollInterval: defaultPollInterval,
		},
		{
			name: "custom config is preserved",
			cfg: &DynamicConfigWatcherConfig{
				ConfigPath:   "/custom/path/config.yaml",
				PollInterval: 10 * time.Second,
			},
			wantConfigPath:   "/custom/path/config.yaml",
			wantPollInterval: 10 * time.Second,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			watcher := NewDynamicConfigWatcher(tt.cfg)

			assert.Equal(t, tt.wantConfigPath, watcher.cfg.ConfigPath)
			assert.Equal(t, tt.wantPollInterval, watcher.cfg.PollInterval)
		})
	}
}

func TestDynamicConfigWatcher_LoadConfig(t *testing.T) {
	originalConfig := globalDynamicConf
	defer func() {
		globalDynamicConfLock.Lock()
		globalDynamicConf = originalConfig
		globalDynamicConfLock.Unlock()
	}()

	tests := []struct {
		name            string
		configContent   string
		wantErr         bool
		wantFreeStarter int64
		wantPaidStarter int64
		fileExists      bool
	}{
		{
			name: "load valid config",
			configContent: `conn_limit:
  free_starter: 100
  paid_starter: 2000`,
			fileExists:      true,
			wantErr:         false,
			wantFreeStarter: 100,
			wantPaidStarter: 2000,
		},
		{
			name:       "missing config file uses defaults",
			fileExists: false,
			wantErr:    false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tmpDir := t.TempDir()
			configPath := filepath.Join(tmpDir, "config.yaml")

			if tt.fileExists {
				err := os.WriteFile(configPath, []byte(tt.configContent), 0644)
				require.NoError(t, err)
			}

			watcher := NewDynamicConfigWatcher(&DynamicConfigWatcherConfig{
				ConfigPath: configPath,
			})
			err := watcher.loadConfig()

			if tt.wantErr {
				require.Error(t, err)
				return
			}

			require.NoError(t, err)

			if tt.wantFreeStarter > 0 {
				config := GlobalDynamicConfig()
				require.NotNil(t, config.ConnLimit)
				assert.Equal(t, tt.wantFreeStarter, config.ConnLimit.FreeStarter)
				assert.Equal(t, tt.wantPaidStarter, config.ConnLimit.PaidStarter)
			}
		})
	}
}

func TestGlobalDynamicConfig(t *testing.T) {
	originalConfig := globalDynamicConf
	defer func() {
		globalDynamicConfLock.Lock()
		globalDynamicConf = originalConfig
		globalDynamicConfLock.Unlock()
	}()

	testConfig := &DynamicConfig{
		ConnLimit: ConnLimit{
			FreeStarter: 999,
			PaidStarter: 9999,
		},
	}

	globalDynamicConfLock.Lock()
	globalDynamicConf = testConfig
	globalDynamicConfLock.Unlock()

	got := GlobalDynamicConfig()
	assert.Equal(t, testConfig, got)
}
