package config

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

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gopkg.in/yaml.v3"
)

func TestDefaultConfig(t *testing.T) {
	cfg := DefaultConfig()
	require.NotNil(t, cfg)

	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, "sqlite", cfg.Database.Driver)
	assert.Equal(t, "", cfg.Database.Host)
	assert.Equal(t, 3306, cfg.Database.Port)
	assert.Equal(t, "", cfg.Database.User)
	assert.Equal(t, "", cfg.Database.Password)
	assert.Equal(t, "nex", cfg.Database.DBName)
	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.Equal(t, true, cfg.Log.Compress)
}

func TestConfig_Validate(t *testing.T) {
	tests := []struct {
		name    string
		modify  func(*Config)
		wantErr bool
		errMsg  string
	}{
		{
			name:    "默认配置有效",
			modify:  func(c *Config) {},
			wantErr: false,
		},
		{
			name:    "端口号为0无效",
			modify:  func(c *Config) { c.Server.Port = 0 },
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name:    "端口号超出范围无效",
			modify:  func(c *Config) { c.Server.Port = 70000 },
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name:    "端口号为1有效",
			modify:  func(c *Config) { c.Server.Port = 1 },
			wantErr: false,
		},
		{
			name:    "端口号为65535有效",
			modify:  func(c *Config) { c.Server.Port = 65535 },
			wantErr: false,
		},
		{
			name:    "无效日志级别",
			modify:  func(c *Config) { c.Log.Level = "invalid" },
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name:    "debug级别有效",
			modify:  func(c *Config) { c.Log.Level = "debug" },
			wantErr: false,
		},
		{
			name:    "warn级别有效",
			modify:  func(c *Config) { c.Log.Level = "warn" },
			wantErr: false,
		},
		{
			name:    "error级别有效",
			modify:  func(c *Config) { c.Log.Level = "error" },
			wantErr: false,
		},
		{
			name:    "SQLite模式路径为空无效",
			modify:  func(c *Config) { c.Database.Path = "" },
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name:    "driver值不合法",
			modify:  func(c *Config) { c.Database.Driver = "postgres" },
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name: "MySQL配置有效",
			modify: func(c *Config) {
				c.Database.Driver = "mysql"
				c.Database.Host = "localhost"
				c.Database.Port = 3306
				c.Database.User = "root"
				c.Database.DBName = "nex"
				c.Database.Path = ""
			},
			wantErr: false,
		},
		{
			name: "MySQL模式host为空无效",
			modify: func(c *Config) {
				c.Database.Driver = "mysql"
				c.Database.Host = ""
				c.Database.User = "root"
				c.Database.DBName = "nex"
				c.Database.Path = ""
			},
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name: "MySQL模式user为空无效",
			modify: func(c *Config) {
				c.Database.Driver = "mysql"
				c.Database.Host = "localhost"
				c.Database.User = ""
				c.Database.DBName = "nex"
				c.Database.Path = ""
			},
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name: "MySQL模式dbname为空无效",
			modify: func(c *Config) {
				c.Database.Driver = "mysql"
				c.Database.Host = "localhost"
				c.Database.User = "root"
				c.Database.DBName = ""
				c.Database.Path = ""
			},
			wantErr: true,
			errMsg:  "配置验证失败",
		},
		{
			name: "MySQL模式忽略path字段",
			modify: func(c *Config) {
				c.Database.Driver = "mysql"
				c.Database.Host = "localhost"
				c.Database.User = "root"
				c.Database.DBName = "nex"
				c.Database.Path = ""
			},
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cfg := DefaultConfig()
			tt.modify(cfg)
			err := cfg.Validate()
			if tt.wantErr {
				assert.Error(t, err)
				if err != nil {
					assert.Contains(t, err.Error(), tt.errMsg)
				}
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestGetConfigDir(t *testing.T) {
	dir, err := GetConfigDir()
	require.NoError(t, err)
	assert.NotEmpty(t, dir)
	assert.Contains(t, dir, ".nex")
}

func TestGetDBPath(t *testing.T) {
	path, err := GetDBPath()
	require.NoError(t, err)
	assert.NotEmpty(t, path)
	assert.Contains(t, path, "config.db")
}

func TestGetConfigPath(t *testing.T) {
	path, err := GetConfigPath()
	require.NoError(t, err)
	assert.NotEmpty(t, path)
	assert.Contains(t, path, "config.yaml")
}

func TestSaveAndLoadConfig(t *testing.T) {
	// 使用临时目录覆盖配置路径
	dir := t.TempDir()

	cfg := &Config{
		Server: ServerConfig{
			Port:         9999,
			ReadTimeout:  10 * time.Second,
			WriteTimeout: 20 * time.Second,
		},
		Database: DatabaseConfig{
			Driver:          "sqlite",
			Path:            filepath.Join(dir, "test.db"),
			Port:            3306,
			DBName:          "nex",
			MaxIdleConns:    5,
			MaxOpenConns:    50,
			ConnMaxLifetime: 30 * time.Minute,
		},
		Log: LogConfig{
			Level:      "debug",
			Path:       filepath.Join(dir, "log"),
			MaxSize:    50,
			MaxBackups: 5,
			MaxAge:     7,
			Compress:   false,
		},
	}

	// 保存配置
	configPath := filepath.Join(dir, "config.yaml")
	data, err := yaml.Marshal(cfg)
	require.NoError(t, err)
	err = os.WriteFile(configPath, data, 0o600)
	require.NoError(t, err)

	// 加载配置
	data, err = os.ReadFile(configPath)
	require.NoError(t, err)
	loaded := &Config{}
	err = yaml.Unmarshal(data, loaded)
	require.NoError(t, err)

	assert.Equal(t, cfg.Server.Port, loaded.Server.Port)
	assert.Equal(t, cfg.Log.Level, loaded.Log.Level)
	assert.Equal(t, cfg.Database.MaxIdleConns, loaded.Database.MaxIdleConns)
	assert.Equal(t, cfg.Log.Compress, loaded.Log.Compress)
}

func TestCLIConfig(t *testing.T) {
	// 测试 CLI 参数配置（简化版本）
	// 注意：由于 flag.Parse 只能调用一次，这里只测试配置加载流程
	t.Run("配置加载流程", func(t *testing.T) {
		// 使用默认配置路径测试
		cfg := DefaultConfig()
		require.NotNil(t, cfg)

		// 验证默认值正确
		assert.Equal(t, 9826, cfg.Server.Port)
		assert.Equal(t, "info", cfg.Log.Level)
	})
}

func TestEnvConfig(t *testing.T) {
	// 测试环境变量配置（简化版本）
	t.Run("环境变量前缀", func(t *testing.T) {
		// 验证环境变量前缀设置正确
		// 实际的环境变量测试需要独立的进程，这里只验证配置结构
		cfg := DefaultConfig()
		require.NotNil(t, cfg)
		assert.Equal(t, 9826, cfg.Server.Port)
	})
}

func TestConfigPriority(t *testing.T) {
	// 测试配置优先级（简化版本）
	t.Run("默认值设置", func(t *testing.T) {
		cfg := DefaultConfig()
		require.NotNil(t, cfg)

		// 验证所有默认值
		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, "sqlite", cfg.Database.Driver)
		assert.Equal(t, 3306, cfg.Database.Port)
		assert.Equal(t, "nex", cfg.Database.DBName)
		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.Equal(t, true, cfg.Log.Compress)
	})
}

func TestPrintSummary(t *testing.T) {
	t.Run("SQLite模式摘要", func(t *testing.T) {
		cfg := DefaultConfig()
		assert.NotPanics(t, func() {
			cfg.PrintSummary(zap.NewNop())
		})
	})
	t.Run("MySQL模式摘要", func(t *testing.T) {
		cfg := DefaultConfig()
		cfg.Database.Driver = "mysql"
		cfg.Database.Host = "db.example.com"
		cfg.Database.Port = 3306
		cfg.Database.User = "nex"
		cfg.Database.DBName = "nex"
		assert.NotPanics(t, func() {
			cfg.PrintSummary(zap.NewNop())
		})
	})
}
