package config

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

	"nex/backend/internal/config"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gopkg.in/yaml.v3"
)

func TestLoadConfig_DefaultValues(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	cfg, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)

	assert.Equal(t, 9826, cfg.Server.Port)
	assert.Equal(t, 30*time.Second, cfg.Server.ReadTimeout)
	assert.Equal(t, 30*time.Second, cfg.Server.WriteTimeout)

	assert.Equal(t, 10, cfg.Database.MaxIdleConns)
	assert.Equal(t, 100, cfg.Database.MaxOpenConns)
	assert.Equal(t, 1*time.Hour, cfg.Database.ConnMaxLifetime)

	assert.Equal(t, "info", cfg.Log.Level)
	assert.Equal(t, 100, cfg.Log.MaxSize)
	assert.Equal(t, 10, cfg.Log.MaxBackups)
	assert.Equal(t, 30, cfg.Log.MaxAge)
	assert.True(t, cfg.Log.Compress)
}

func TestLoadConfig_EnvOverride(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	t.Setenv("NEX_SERVER_PORT", "9000")
	t.Setenv("NEX_LOG_LEVEL", "debug")
	t.Setenv("NEX_DATABASE_MAX_IDLE_CONNS", "20")

	cfg, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)

	assert.Equal(t, 9000, cfg.Server.Port)
	assert.Equal(t, "debug", cfg.Log.Level)
	assert.Equal(t, 20, cfg.Database.MaxIdleConns)
}

func TestLoadConfig_YAMLFile(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
server:
  port: 8080
  read_timeout: 60s
  write_timeout: 60s
database:
  path: /custom/path.db
  max_idle_conns: 5
  max_open_conns: 50
  conn_max_lifetime: 2h
log:
  level: warn
  path: /custom/log
  max_size: 200
  max_backups: 5
  max_age: 7
  compress: false
`
	err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
	require.NoError(t, err)

	cfg, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)

	assert.Equal(t, 8080, cfg.Server.Port)
	assert.Equal(t, 60*time.Second, cfg.Server.ReadTimeout)
	assert.Equal(t, 60*time.Second, cfg.Server.WriteTimeout)
	assert.Equal(t, "/custom/path.db", cfg.Database.Path)
	assert.Equal(t, 5, cfg.Database.MaxIdleConns)
	assert.Equal(t, 50, cfg.Database.MaxOpenConns)
	assert.Equal(t, 2*time.Hour, cfg.Database.ConnMaxLifetime)
	assert.Equal(t, "warn", cfg.Log.Level)
	assert.Equal(t, "/custom/log", cfg.Log.Path)
	assert.Equal(t, 200, cfg.Log.MaxSize)
	assert.Equal(t, 5, cfg.Log.MaxBackups)
	assert.Equal(t, 7, cfg.Log.MaxAge)
	assert.False(t, cfg.Log.Compress)
}

func TestLoadConfig_PriorityChain(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	yamlContent := `
server:
  port: 8080
log:
  level: warn
`
	err := os.WriteFile(configPath, []byte(yamlContent), 0o600)
	require.NoError(t, err)

	t.Setenv("NEX_SERVER_PORT", "9000")

	originalArgs := os.Args
	defer func() { os.Args = originalArgs }()
	os.Args = []string{"test", "--server-port", "9999"}

	cfg, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)

	assert.Equal(t, 9999, cfg.Server.Port, "CLI should override ENV and YAML")
	assert.Equal(t, "warn", cfg.Log.Level, "YAML value should be used when no CLI/ENV override")
}

func TestLoadConfig_AutoCreate(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	_, err := os.Stat(configPath)
	assert.True(t, os.IsNotExist(err), "config file should not exist before load")

	cfg, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)
	require.NotNil(t, cfg)

	assert.Equal(t, 9826, cfg.Server.Port, "should load with default values")
}

func TestSaveAndLoadConfig(t *testing.T) {
	tmpDir := t.TempDir()
	configPath := filepath.Join(tmpDir, "config.yaml")

	cfg := &config.Config{
		Server: config.ServerConfig{
			Port:         7777,
			ReadTimeout:  45 * time.Second,
			WriteTimeout: 45 * time.Second,
		},
		Database: config.DatabaseConfig{
			Driver:          "sqlite",
			Path:            filepath.Join(tmpDir, "test.db"),
			Port:            3306,
			DBName:          "nex",
			MaxIdleConns:    15,
			MaxOpenConns:    150,
			ConnMaxLifetime: 2 * time.Hour,
		},
		Log: config.LogConfig{
			Level:      "debug",
			Path:       filepath.Join(tmpDir, "log"),
			MaxSize:    50,
			MaxBackups: 3,
			MaxAge:     14,
			Compress:   false,
		},
	}

	data, err := yaml.Marshal(cfg)
	require.NoError(t, err)

	err = os.WriteFile(configPath, data, 0o600)
	require.NoError(t, err)

	loaded, err := config.LoadConfigFromPath(configPath)
	require.NoError(t, err)

	assert.Equal(t, cfg.Server.Port, loaded.Server.Port)
	assert.Equal(t, cfg.Server.ReadTimeout, loaded.Server.ReadTimeout)
	assert.Equal(t, cfg.Server.WriteTimeout, loaded.Server.WriteTimeout)
	assert.Equal(t, cfg.Database.MaxIdleConns, loaded.Database.MaxIdleConns)
	assert.Equal(t, cfg.Database.MaxOpenConns, loaded.Database.MaxOpenConns)
	assert.Equal(t, cfg.Database.ConnMaxLifetime, loaded.Database.ConnMaxLifetime)
	assert.Equal(t, cfg.Log.Level, loaded.Log.Level)
	assert.Equal(t, cfg.Log.MaxSize, loaded.Log.MaxSize)
	assert.Equal(t, cfg.Log.MaxBackups, loaded.Log.MaxBackups)
	assert.Equal(t, cfg.Log.MaxAge, loaded.Log.MaxAge)
	assert.Equal(t, cfg.Log.Compress, loaded.Log.Compress)
}
