// Package config provides configuration management for AutoSSO.
// This file contains property-based tests for configuration loading.
package config

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

	"gopkg.in/yaml.v3"
	"pgregory.net/rapid"
)

// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// For any valid configuration file, loading the config and then serializing it
// should produce an equivalent configuration structure.
//
// **Validates: Requirements 2.1-2.8, 9.1**

// genNonEmptyString generates a non-empty alphanumeric string suitable for config values.
func genNonEmptyString() *rapid.Generator[string] {
	return rapid.StringMatching(`[a-zA-Z][a-zA-Z0-9_-]{0,30}`)
}

// genOptionalString generates an optional string (can be empty).
func genOptionalString() *rapid.Generator[string] {
	return rapid.OneOf(
		rapid.Just(""),
		genNonEmptyString(),
	)
}

// genPositiveInt generates a positive integer for config values.
func genPositiveInt() *rapid.Generator[int] {
	return rapid.IntRange(1, 65535)
}

// genDuration generates a valid duration for config values.
func genDuration() *rapid.Generator[time.Duration] {
	return rapid.Custom(func(t *rapid.T) time.Duration {
		// Generate durations between 1 second and 24 hours
		seconds := rapid.IntRange(1, 86400).Draw(t, "seconds")
		return time.Duration(seconds) * time.Second
	})
}

// genLogLevel generates a valid log level.
func genLogLevel() *rapid.Generator[string] {
	return rapid.SampledFrom([]string{"debug", "info", "warn", "error"})
}

// genAccountType generates a valid account type.
func genAccountType() *rapid.Generator[string] {
	return rapid.SampledFrom([]string{"prod", "test", "dev", "staging"})
}

// genCloudAccount generates a valid CloudAccount.
func genCloudAccount() *rapid.Generator[CloudAccount] {
	return rapid.Custom(func(t *rapid.T) CloudAccount {
		return CloudAccount{
			ID:         genNonEmptyString().Draw(t, "id"),
			Name:       genNonEmptyString().Draw(t, "name"),
			Type:       genAccountType().Draw(t, "type"),
			RoleSuffix: genOptionalString().Draw(t, "role_suffix"),
		}
	})
}

// genCloudAccounts generates a slice of CloudAccounts.
func genCloudAccounts() *rapid.Generator[[]CloudAccount] {
	return rapid.SliceOfN(genCloudAccount(), 0, 5)
}

// genFeishuConfig generates a valid FeishuConfig.
func genFeishuConfig() *rapid.Generator[FeishuConfig] {
	return rapid.Custom(func(t *rapid.T) FeishuConfig {
		return FeishuConfig{
			AppID:        genNonEmptyString().Draw(t, "app_id"),
			AppSecret:    genNonEmptyString().Draw(t, "app_secret"),
			ApprovalCode: genNonEmptyString().Draw(t, "approval_code"),
			ChatID:       genNonEmptyString().Draw(t, "chat_id"),
			BaseURL:      "https://" + genNonEmptyString().Draw(t, "base_url_host") + ".example.com",
		}
	})
}

// genDatabaseConfig generates a valid DatabaseConfig.
func genDatabaseConfig() *rapid.Generator[DatabaseConfig] {
	return rapid.Custom(func(t *rapid.T) DatabaseConfig {
		return DatabaseConfig{
			Host:         genNonEmptyString().Draw(t, "host"),
			Port:         genPositiveInt().Draw(t, "port"),
			User:         genNonEmptyString().Draw(t, "user"),
			Password:     genNonEmptyString().Draw(t, "password"),
			Database:     genNonEmptyString().Draw(t, "database"),
			MaxOpenConns: rapid.IntRange(1, 100).Draw(t, "max_open_conns"),
			MaxIdleConns: rapid.IntRange(1, 50).Draw(t, "max_idle_conns"),
			MaxLifetime:  genDuration().Draw(t, "max_lifetime"),
		}
	})
}

// genAWSConfig generates a valid AWSConfig.
func genAWSConfig() *rapid.Generator[AWSConfig] {
	return rapid.Custom(func(t *rapid.T) AWSConfig {
		return AWSConfig{
			IdentityCenterARN: "arn:aws:sso:::instance/" + genNonEmptyString().Draw(t, "instance_id"),
			IdentityStoreID:   "d-" + genNonEmptyString().Draw(t, "store_id"),
			Region:            genNonEmptyString().Draw(t, "region"),
			Accounts:          genCloudAccounts().Draw(t, "accounts"),
		}
	})
}

// genGCPConfig generates a valid GCPConfig.
func genGCPConfig() *rapid.Generator[GCPConfig] {
	return rapid.Custom(func(t *rapid.T) GCPConfig {
		return GCPConfig{
			CredentialsFile: "./" + genNonEmptyString().Draw(t, "creds_file") + ".json",
			RoleMappingFile: "./" + genNonEmptyString().Draw(t, "role_file") + ".ini",
			Projects:        genCloudAccounts().Draw(t, "projects"),
		}
	})
}

// genAzureConfig generates a valid AzureConfig.
func genAzureConfig() *rapid.Generator[AzureConfig] {
	return rapid.Custom(func(t *rapid.T) AzureConfig {
		return AzureConfig{
			TenantID:      genNonEmptyString().Draw(t, "tenant_id"),
			ClientID:      genNonEmptyString().Draw(t, "client_id"),
			ClientSecret:  genNonEmptyString().Draw(t, "client_secret"),
			Subscriptions: genCloudAccounts().Draw(t, "subscriptions"),
		}
	})
}

// genAlibabaConfig generates a valid AlibabaConfig.
func genAlibabaConfig() *rapid.Generator[AlibabaConfig] {
	return rapid.Custom(func(t *rapid.T) AlibabaConfig {
		return AlibabaConfig{
			DirectoryID:     "d-" + genNonEmptyString().Draw(t, "directory_id"),
			Endpoint:        genNonEmptyString().Draw(t, "endpoint") + ".aliyuncs.com",
			Accounts:        genCloudAccounts().Draw(t, "accounts"),
		}
	})
}

// genProvidersConfig generates a valid ProvidersConfig.
func genProvidersConfig() *rapid.Generator[ProvidersConfig] {
	return rapid.Custom(func(t *rapid.T) ProvidersConfig {
		return ProvidersConfig{
			AWS:     genAWSConfig().Draw(t, "aws"),
			GCP:     genGCPConfig().Draw(t, "gcp"),
			Azure:   genAzureConfig().Draw(t, "azure"),
			Alibaba: genAlibabaConfig().Draw(t, "alibaba"),
		}
	})
}

// genRuntimeConfig generates a valid RuntimeConfig.
func genRuntimeConfig() *rapid.Generator[RuntimeConfig] {
	return rapid.Custom(func(t *rapid.T) RuntimeConfig {
		return RuntimeConfig{
			PollInterval:        genDuration().Draw(t, "poll_interval"),
			ExpireCheckInterval: genDuration().Draw(t, "expire_check_interval"),
			GracePeriodDays:     rapid.IntRange(1, 30).Draw(t, "grace_period_days"),
			HTTPTimeout:         genDuration().Draw(t, "http_timeout"),
			MaxRetries:          rapid.IntRange(1, 10).Draw(t, "max_retries"),
			LogLevel:            genLogLevel().Draw(t, "log_level"),
			WhitelistEmails:     rapid.SliceOfN(genNonEmptyString(), 0, 5).Draw(t, "whitelist_emails"),
			GeneralPermissionSets: rapid.SliceOfN(genNonEmptyString(), 0, 5).Draw(t, "general_permission_sets"),
		}
	})
}

// genPermissionMapping generates a valid PermissionMapping.
func genPermissionMapping() *rapid.Generator[PermissionMapping] {
	return rapid.Custom(func(t *rapid.T) PermissionMapping {
		return PermissionMapping{
			FeishuName: genNonEmptyString().Draw(t, "feishu_name"),
			RealName:   genNonEmptyString().Draw(t, "real_name"),
		}
	})
}

// genGCPRoleMappingConfig generates a valid GCPRoleMapping for config testing.
func genGCPRoleMappingConfig() *rapid.Generator[GCPRoleMapping] {
	return rapid.Custom(func(t *rapid.T) GCPRoleMapping {
		numRoles := rapid.IntRange(1, 5).Draw(t, "num_roles")
		roles := make([]string, numRoles)
		for i := 0; i < numRoles; i++ {
			roles[i] = "roles/" + genNonEmptyString().Draw(t, "gcp_role")
		}
		return GCPRoleMapping{
			CustomRole: genNonEmptyString().Draw(t, "custom_role"),
			GCPRoles:   roles,
		}
	})
}

// genConfig generates a valid Config.
func genConfig() *rapid.Generator[Config] {
	return rapid.Custom(func(t *rapid.T) Config {
		return Config{
			Feishu:             genFeishuConfig().Draw(t, "feishu"),
			Database:           genDatabaseConfig().Draw(t, "database"),
			Providers:          genProvidersConfig().Draw(t, "providers"),
			Runtime:            genRuntimeConfig().Draw(t, "runtime"),
			PermissionMappings: rapid.SliceOfN(genPermissionMapping(), 0, 10).Draw(t, "permission_mappings"),
			GCPRoleMappings:    rapid.SliceOfN(genGCPRoleMappingConfig(), 0, 10).Draw(t, "gcp_role_mappings"),
		}
	})
}

// configsEqual compares two Config structs for equality.
// It handles special cases like empty slices vs nil slices.
func configsEqual(a, b *Config) bool {
	return reflect.DeepEqual(a, b)
}

// TestConfigLoadRoundTrip tests that loading a config and serializing it produces
// an equivalent configuration structure.
//
// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// **Validates: Requirements 2.1-2.8, 9.1**
func TestConfigLoadRoundTrip(t *testing.T) {
	// Create a temporary directory for all test iterations
	tmpDir := t.TempDir()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate a random valid configuration
		originalConfig := genConfig().Draw(rt, "config")

		// Create a unique config file for this iteration
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")

		// Serialize the config to YAML
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}

		// Write the YAML to a file
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Load the config back using the Load function
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Compare the original and loaded configs
		if !configsEqual(&originalConfig, loadedConfig) {
			rt.Fatalf("Config round-trip failed:\nOriginal: %+v\nLoaded: %+v", originalConfig, loadedConfig)
		}
	})
}

// TestConfigLoadRoundTripWithDefaults tests that configs with default values
// are correctly preserved through the round-trip.
//
// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// **Validates: Requirements 2.1-2.8, 9.1**
func TestConfigLoadRoundTripWithDefaults(t *testing.T) {
	// Create a temporary directory for all test iterations
	tmpDir := t.TempDir()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate a config with some fields that might use defaults
		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        genNonEmptyString().Draw(rt, "app_id"),
				AppSecret:    genNonEmptyString().Draw(rt, "app_secret"),
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn", // Default value
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1", // Default value
				Port:         3306,        // Default value
				User:         genNonEmptyString().Draw(rt, "db_user"),
				Password:     genNonEmptyString().Draw(rt, "db_password"),
				Database:     "security", // Default value
				MaxOpenConns: 10,         // Default value
				MaxIdleConns: 5,          // Default value
				MaxLifetime:  time.Hour,  // Default value
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/" + genNonEmptyString().Draw(rt, "aws_instance"),
					IdentityStoreID:   "d-" + genNonEmptyString().Draw(rt, "aws_store"),
					Region:            "ap-northeast-1", // Default value
					Accounts:          []CloudAccount{},
				},
				GCP: GCPConfig{
					CredentialsFile: "./" + genNonEmptyString().Draw(rt, "gcp_creds") + ".json",
					RoleMappingFile: "./" + genNonEmptyString().Draw(rt, "gcp_roles") + ".ini",
					Projects:        []CloudAccount{},
				},
				Azure: AzureConfig{
					TenantID:      genNonEmptyString().Draw(rt, "azure_tenant"),
					ClientID:      genNonEmptyString().Draw(rt, "azure_client"),
					ClientSecret:  genNonEmptyString().Draw(rt, "azure_secret"),
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-" + genNonEmptyString().Draw(rt, "alibaba_dir"),
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com", // Default value
					Accounts:        []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,  // Default value
				ExpireCheckInterval: time.Hour,        // Default value
				GracePeriodDays:     3,                // Default value
				HTTPTimeout:         10 * time.Second, // Default value
				MaxRetries:          3,                // Default value
				LogLevel:            "info",           // Default value
				WhitelistEmails:     []string{},
				GeneralPermissionSets: []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Create a unique config file for this iteration
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")

		// Serialize the config to YAML
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}

		// Write the YAML to a file
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Load the config back
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Compare the original and loaded configs
		if !configsEqual(&originalConfig, loadedConfig) {
			rt.Fatalf("Config round-trip with defaults failed:\nOriginal: %+v\nLoaded: %+v", originalConfig, loadedConfig)
		}
	})
}

// TestConfigLoadRoundTripCloudAccounts tests that cloud account configurations
// are correctly preserved through the round-trip.
//
// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// **Validates: Requirements 2.1-2.8, 9.1**
func TestConfigLoadRoundTripCloudAccounts(t *testing.T) {
	// Create a temporary directory for all test iterations
	tmpDir := t.TempDir()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate cloud accounts with various configurations
		awsAccounts := rapid.SliceOfN(genCloudAccount(), 1, 10).Draw(rt, "aws_accounts")
		gcpProjects := rapid.SliceOfN(genCloudAccount(), 1, 10).Draw(rt, "gcp_projects")
		azureSubs := rapid.SliceOfN(genCloudAccount(), 1, 10).Draw(rt, "azure_subs")
		alibabaAccounts := rapid.SliceOfN(genCloudAccount(), 1, 10).Draw(rt, "alibaba_accounts")

		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        genNonEmptyString().Draw(rt, "app_id"),
				AppSecret:    genNonEmptyString().Draw(rt, "app_secret"),
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         genNonEmptyString().Draw(rt, "db_user"),
				Password:     genNonEmptyString().Draw(rt, "db_password"),
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/test",
					IdentityStoreID:   "d-test",
					Region:            "ap-northeast-1",
					Accounts:          awsAccounts,
				},
				GCP: GCPConfig{
					CredentialsFile: "./creds.json",
					RoleMappingFile: "./roles.ini",
					Projects:        gcpProjects,
				},
				Azure: AzureConfig{
					TenantID:      genNonEmptyString().Draw(rt, "azure_tenant"),
					ClientID:      genNonEmptyString().Draw(rt, "azure_client"),
					ClientSecret:  genNonEmptyString().Draw(rt, "azure_secret"),
					Subscriptions: azureSubs,
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-test",
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        alibabaAccounts,
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:       []string{},
				GeneralPermissionSets: []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Create a unique config file for this iteration
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")

		// Serialize the config to YAML
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}

		// Write the YAML to a file
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Load the config back
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify AWS accounts
		if len(loadedConfig.Providers.AWS.Accounts) != len(awsAccounts) {
			rt.Fatalf("AWS accounts count mismatch: expected %d, got %d",
				len(awsAccounts), len(loadedConfig.Providers.AWS.Accounts))
		}
		for i, acc := range awsAccounts {
			loaded := loadedConfig.Providers.AWS.Accounts[i]
			if acc.ID != loaded.ID || acc.Name != loaded.Name || acc.Type != loaded.Type {
				rt.Fatalf("AWS account %d mismatch: expected %+v, got %+v", i, acc, loaded)
			}
		}

		// Verify GCP projects
		if len(loadedConfig.Providers.GCP.Projects) != len(gcpProjects) {
			rt.Fatalf("GCP projects count mismatch: expected %d, got %d",
				len(gcpProjects), len(loadedConfig.Providers.GCP.Projects))
		}

		// Verify Azure subscriptions
		if len(loadedConfig.Providers.Azure.Subscriptions) != len(azureSubs) {
			rt.Fatalf("Azure subscriptions count mismatch: expected %d, got %d",
				len(azureSubs), len(loadedConfig.Providers.Azure.Subscriptions))
		}

		// Verify Alibaba accounts
		if len(loadedConfig.Providers.Alibaba.Accounts) != len(alibabaAccounts) {
			rt.Fatalf("Alibaba accounts count mismatch: expected %d, got %d",
				len(alibabaAccounts), len(loadedConfig.Providers.Alibaba.Accounts))
		}
	})
}

// TestConfigLoadRoundTripPermissionMappings tests that permission mappings
// are correctly preserved through the round-trip.
//
// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// **Validates: Requirements 2.1-2.8, 9.1**
func TestConfigLoadRoundTripPermissionMappings(t *testing.T) {
	// Create a temporary directory for all test iterations
	tmpDir := t.TempDir()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate permission mappings
		mappings := rapid.SliceOfN(genPermissionMapping(), 1, 20).Draw(rt, "mappings")

		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        genNonEmptyString().Draw(rt, "app_id"),
				AppSecret:    genNonEmptyString().Draw(rt, "app_secret"),
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         genNonEmptyString().Draw(rt, "db_user"),
				Password:     genNonEmptyString().Draw(rt, "db_password"),
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					Region:   "ap-northeast-1",
					Accounts: []CloudAccount{},
				},
				GCP: GCPConfig{
					Projects: []CloudAccount{},
				},
				Azure: AzureConfig{
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					Endpoint: "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts: []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: mappings,
		}

		// Create a unique config file for this iteration
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")

		// Serialize the config to YAML
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}

		// Write the YAML to a file
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Load the config back
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify permission mappings
		if len(loadedConfig.PermissionMappings) != len(mappings) {
			rt.Fatalf("Permission mappings count mismatch: expected %d, got %d",
				len(mappings), len(loadedConfig.PermissionMappings))
		}

		for i, mapping := range mappings {
			loaded := loadedConfig.PermissionMappings[i]
			if mapping.FeishuName != loaded.FeishuName || mapping.RealName != loaded.RealName {
				rt.Fatalf("Permission mapping %d mismatch: expected %+v, got %+v", i, mapping, loaded)
			}
		}
	})
}

// TestConfigLoadRoundTripDurations tests that duration values are correctly
// preserved through the round-trip.
//
// Feature: autosso-refactoring, Property 1: 配置加载往返一致性
// **Validates: Requirements 2.1-2.8, 9.1**
func TestConfigLoadRoundTripDurations(t *testing.T) {
	// Create a temporary directory for all test iterations
	tmpDir := t.TempDir()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate various duration values
		pollInterval := genDuration().Draw(rt, "poll_interval")
		expireCheckInterval := genDuration().Draw(rt, "expire_check_interval")
		httpTimeout := genDuration().Draw(rt, "http_timeout")
		maxLifetime := genDuration().Draw(rt, "max_lifetime")

		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        genNonEmptyString().Draw(rt, "app_id"),
				AppSecret:    genNonEmptyString().Draw(rt, "app_secret"),
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         genNonEmptyString().Draw(rt, "db_user"),
				Password:     genNonEmptyString().Draw(rt, "db_password"),
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  maxLifetime,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					Region:   "ap-northeast-1",
					Accounts: []CloudAccount{},
				},
				GCP: GCPConfig{
					Projects: []CloudAccount{},
				},
				Azure: AzureConfig{
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					Endpoint: "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts: []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        pollInterval,
				ExpireCheckInterval: expireCheckInterval,
				GracePeriodDays:     3,
				HTTPTimeout:         httpTimeout,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Create a unique config file for this iteration
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")

		// Serialize the config to YAML
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}

		// Write the YAML to a file
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Load the config back
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify duration values
		if loadedConfig.Runtime.PollInterval != pollInterval {
			rt.Fatalf("PollInterval mismatch: expected %v, got %v",
				pollInterval, loadedConfig.Runtime.PollInterval)
		}
		if loadedConfig.Runtime.ExpireCheckInterval != expireCheckInterval {
			rt.Fatalf("ExpireCheckInterval mismatch: expected %v, got %v",
				expireCheckInterval, loadedConfig.Runtime.ExpireCheckInterval)
		}
		if loadedConfig.Runtime.HTTPTimeout != httpTimeout {
			rt.Fatalf("HTTPTimeout mismatch: expected %v, got %v",
				httpTimeout, loadedConfig.Runtime.HTTPTimeout)
		}
		if loadedConfig.Database.MaxLifetime != maxLifetime {
			rt.Fatalf("MaxLifetime mismatch: expected %v, got %v",
				maxLifetime, loadedConfig.Database.MaxLifetime)
		}
	})
}

// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// For any configuration item, when both config file value and environment variable
// value exist, the environment variable value should take precedence.
//
// **Validates: Requirements 2.1-2.5, 9.2**

// envVarTestCase represents a test case for environment variable override testing.
type envVarTestCase struct {
	envName    string
	configPath func(*Config) *string
}

// getEnvVarTestCases returns all environment variable test cases.
// These are the fields that support env var override (marked with `env:` tag).
func getEnvVarTestCases() []envVarTestCase {
	return []envVarTestCase{
		// Feishu config - Requirements 2.1
		{
			envName:    "FEISHU_APP_ID",
			configPath: func(c *Config) *string { return &c.Feishu.AppID },
		},
		{
			envName:    "FEISHU_APP_SECRET",
			configPath: func(c *Config) *string { return &c.Feishu.AppSecret },
		},
		// Database config - Requirements 2.4
		{
			envName:    "DB_USER",
			configPath: func(c *Config) *string { return &c.Database.User },
		},
		{
			envName:    "DB_PASSWORD",
			configPath: func(c *Config) *string { return &c.Database.Password },
		},
		// Azure config - Requirements 2.2
		{
			envName:    "AZURE_TENANT_ID",
			configPath: func(c *Config) *string { return &c.Providers.Azure.TenantID },
		},
		{
			envName:    "AZURE_CLIENT_ID",
			configPath: func(c *Config) *string { return &c.Providers.Azure.ClientID },
		},
		{
			envName:    "AZURE_CLIENT_SECRET",
			configPath: func(c *Config) *string { return &c.Providers.Azure.ClientSecret },
		},

	}
}

// TestEnvVarOverridesConfigFileValue tests that environment variables take precedence
// over config file values for all fields that support env var override.
//
// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// **Validates: Requirements 2.1-2.5, 9.2**
func TestEnvVarOverridesConfigFileValue(t *testing.T) {
	tmpDir := t.TempDir()
	testCases := getEnvVarTestCases()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate random config file values and env var values
		configFileValues := make(map[string]string)
		envVarValues := make(map[string]string)

		for _, tc := range testCases {
			// Generate distinct values for config file and env var
			configFileValues[tc.envName] = "config_" + genNonEmptyString().Draw(rt, "config_"+tc.envName)
			envVarValues[tc.envName] = "env_" + genNonEmptyString().Draw(rt, "env_"+tc.envName)
		}

		// Create a config with the config file values
		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        configFileValues["FEISHU_APP_ID"],
				AppSecret:    configFileValues["FEISHU_APP_SECRET"],
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         configFileValues["DB_USER"],
				Password:     configFileValues["DB_PASSWORD"],
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/test",
					IdentityStoreID:   "d-test",
					Region:            "ap-northeast-1",
					Accounts:          []CloudAccount{},
				},
				GCP: GCPConfig{
					CredentialsFile: "./creds.json",
					RoleMappingFile: "./roles.ini",
					Projects:        []CloudAccount{},
				},
				Azure: AzureConfig{
					TenantID:      configFileValues["AZURE_TENANT_ID"],
					ClientID:      configFileValues["AZURE_CLIENT_ID"],
					ClientSecret:  configFileValues["AZURE_CLIENT_SECRET"],
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-test",
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Write config to file
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Set environment variables
		for _, tc := range testCases {
			os.Setenv(tc.envName, envVarValues[tc.envName])
		}
		// Clean up environment variables after test
		defer func() {
			for _, tc := range testCases {
				os.Unsetenv(tc.envName)
			}
		}()

		// Load config
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify that environment variable values take precedence
		for _, tc := range testCases {
			actualValue := *tc.configPath(loadedConfig)
			expectedValue := envVarValues[tc.envName]

			if actualValue != expectedValue {
				rt.Fatalf("Environment variable %s did not override config file value.\n"+
					"Expected (env var): %s\n"+
					"Got: %s\n"+
					"Config file value was: %s",
					tc.envName, expectedValue, actualValue, configFileValues[tc.envName])
			}
		}
	})
}

// TestEnvVarOverridesSingleField tests that each individual environment variable
// correctly overrides its corresponding config file value.
//
// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// **Validates: Requirements 2.1-2.5, 9.2**
func TestEnvVarOverridesSingleField(t *testing.T) {
	tmpDir := t.TempDir()
	testCases := getEnvVarTestCases()

	for _, tc := range testCases {
		tc := tc // capture range variable
		t.Run(tc.envName, func(t *testing.T) {
			rapid.Check(t, func(rt *rapid.T) {
				// Generate distinct values
				configFileValue := "config_" + genNonEmptyString().Draw(rt, "config_value")
				envVarValue := "env_" + genNonEmptyString().Draw(rt, "env_value")

				// Create a minimal config with the config file value
				originalConfig := createMinimalConfig()
				*tc.configPath(&originalConfig) = configFileValue

				// Write config to file
				configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")
				yamlData, err := yaml.Marshal(&originalConfig)
				if err != nil {
					rt.Fatalf("Failed to marshal config to YAML: %v", err)
				}
				if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
					rt.Fatalf("Failed to write config file: %v", err)
				}
				defer os.Remove(configPath)

				// Set the environment variable
				os.Setenv(tc.envName, envVarValue)
				defer os.Unsetenv(tc.envName)

				// Load config
				loadedConfig, err := Load(configPath)
				if err != nil {
					rt.Fatalf("Failed to load config: %v", err)
				}

				// Verify that environment variable value takes precedence
				actualValue := *tc.configPath(loadedConfig)
				if actualValue != envVarValue {
					rt.Fatalf("Environment variable %s did not override config file value.\n"+
						"Expected (env var): %s\n"+
						"Got: %s\n"+
						"Config file value was: %s",
						tc.envName, envVarValue, actualValue, configFileValue)
				}
			})
		})
	}
}

// TestEnvVarOverridesWithRandomValues tests environment variable override with
// completely random values including special characters.
//
// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// **Validates: Requirements 2.1-2.5, 9.2**
func TestEnvVarOverridesWithRandomValues(t *testing.T) {
	tmpDir := t.TempDir()
	testCases := getEnvVarTestCases()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate random values (alphanumeric to avoid YAML parsing issues)
		configFileValues := make(map[string]string)
		envVarValues := make(map[string]string)

		for _, tc := range testCases {
			// Use alphanumeric strings with different prefixes to ensure they're distinct
			configFileValues[tc.envName] = "file_" + rapid.StringMatching(`[a-zA-Z0-9]{5,20}`).Draw(rt, "config_"+tc.envName)
			envVarValues[tc.envName] = "envv_" + rapid.StringMatching(`[a-zA-Z0-9]{5,20}`).Draw(rt, "env_"+tc.envName)
		}

		// Create config with config file values
		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        configFileValues["FEISHU_APP_ID"],
				AppSecret:    configFileValues["FEISHU_APP_SECRET"],
				ApprovalCode: "test-approval-code",
				ChatID:       "test-chat-id",
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         configFileValues["DB_USER"],
				Password:     configFileValues["DB_PASSWORD"],
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/test",
					IdentityStoreID:   "d-test",
					Region:            "ap-northeast-1",
					Accounts:          []CloudAccount{},
				},
				GCP: GCPConfig{
					CredentialsFile: "./creds.json",
					RoleMappingFile: "./roles.ini",
					Projects:        []CloudAccount{},
				},
				Azure: AzureConfig{
					TenantID:      configFileValues["AZURE_TENANT_ID"],
					ClientID:      configFileValues["AZURE_CLIENT_ID"],
					ClientSecret:  configFileValues["AZURE_CLIENT_SECRET"],
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-test",
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Write config to file
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Set environment variables
		for _, tc := range testCases {
			os.Setenv(tc.envName, envVarValues[tc.envName])
		}
		defer func() {
			for _, tc := range testCases {
				os.Unsetenv(tc.envName)
			}
		}()

		// Load config
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify all environment variable values take precedence
		for _, tc := range testCases {
			actualValue := *tc.configPath(loadedConfig)
			expectedValue := envVarValues[tc.envName]

			if actualValue != expectedValue {
				rt.Fatalf("Environment variable %s did not override config file value.\n"+
					"Expected (env var): %s\n"+
					"Got: %s\n"+
					"Config file value was: %s",
					tc.envName, expectedValue, actualValue, configFileValues[tc.envName])
			}
		}
	})
}

// TestEnvVarNotSetUsesConfigFileValue tests that when an environment variable
// is not set, the config file value is used.
//
// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// **Validates: Requirements 2.1-2.5, 9.2**
func TestEnvVarNotSetUsesConfigFileValue(t *testing.T) {
	tmpDir := t.TempDir()
	testCases := getEnvVarTestCases()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate config file values
		configFileValues := make(map[string]string)
		for _, tc := range testCases {
			configFileValues[tc.envName] = "config_" + genNonEmptyString().Draw(rt, "config_"+tc.envName)
		}

		// Create config with config file values
		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        configFileValues["FEISHU_APP_ID"],
				AppSecret:    configFileValues["FEISHU_APP_SECRET"],
				ApprovalCode: "test-approval-code",
				ChatID:       "test-chat-id",
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         configFileValues["DB_USER"],
				Password:     configFileValues["DB_PASSWORD"],
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/test",
					IdentityStoreID:   "d-test",
					Region:            "ap-northeast-1",
					Accounts:          []CloudAccount{},
				},
				GCP: GCPConfig{
					CredentialsFile: "./creds.json",
					RoleMappingFile: "./roles.ini",
					Projects:        []CloudAccount{},
				},
				Azure: AzureConfig{
					TenantID:      configFileValues["AZURE_TENANT_ID"],
					ClientID:      configFileValues["AZURE_CLIENT_ID"],
					ClientSecret:  configFileValues["AZURE_CLIENT_SECRET"],
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-test",
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Write config to file
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Ensure environment variables are NOT set
		for _, tc := range testCases {
			os.Unsetenv(tc.envName)
		}

		// Load config
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify config file values are used when env vars are not set
		for _, tc := range testCases {
			actualValue := *tc.configPath(loadedConfig)
			expectedValue := configFileValues[tc.envName]

			if actualValue != expectedValue {
				rt.Fatalf("Config file value for %s was not used when env var is not set.\n"+
					"Expected (config file): %s\n"+
					"Got: %s",
					tc.envName, expectedValue, actualValue)
			}
		}
	})
}

// TestEnvVarOverridesPartialSet tests that when only some environment variables
// are set, only those fields are overridden.
//
// Feature: autosso-refactoring, Property 2: 环境变量覆盖配置文件值
// **Validates: Requirements 2.1-2.5, 9.2**
func TestEnvVarOverridesPartialSet(t *testing.T) {
	tmpDir := t.TempDir()
	testCases := getEnvVarTestCases()

	rapid.Check(t, func(rt *rapid.T) {
		// Generate config file values and env var values
		configFileValues := make(map[string]string)
		envVarValues := make(map[string]string)
		setEnvVars := make(map[string]bool)

		for _, tc := range testCases {
			configFileValues[tc.envName] = "config_" + genNonEmptyString().Draw(rt, "config_"+tc.envName)
			envVarValues[tc.envName] = "env_" + genNonEmptyString().Draw(rt, "env_"+tc.envName)
			// Randomly decide whether to set this env var
			setEnvVars[tc.envName] = rapid.Bool().Draw(rt, "set_"+tc.envName)
		}

		// Create config with config file values
		originalConfig := Config{
			Feishu: FeishuConfig{
				AppID:        configFileValues["FEISHU_APP_ID"],
				AppSecret:    configFileValues["FEISHU_APP_SECRET"],
				ApprovalCode: "test-approval-code",
				ChatID:       "test-chat-id",
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         configFileValues["DB_USER"],
				Password:     configFileValues["DB_PASSWORD"],
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				AWS: AWSConfig{
					IdentityCenterARN: "arn:aws:sso:::instance/test",
					IdentityStoreID:   "d-test",
					Region:            "ap-northeast-1",
					Accounts:          []CloudAccount{},
				},
				GCP: GCPConfig{
					CredentialsFile: "./creds.json",
					RoleMappingFile: "./roles.ini",
					Projects:        []CloudAccount{},
				},
				Azure: AzureConfig{
					TenantID:      configFileValues["AZURE_TENANT_ID"],
					ClientID:      configFileValues["AZURE_CLIENT_ID"],
					ClientSecret:  configFileValues["AZURE_CLIENT_SECRET"],
					Subscriptions: []CloudAccount{},
				},
				Alibaba: AlibabaConfig{
					DirectoryID:     "d-test",
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        []CloudAccount{},
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Write config to file
		configPath := filepath.Join(tmpDir, "config_"+rapid.StringMatching(`[a-z]{8}`).Draw(rt, "suffix")+".yaml")
		yamlData, err := yaml.Marshal(&originalConfig)
		if err != nil {
			rt.Fatalf("Failed to marshal config to YAML: %v", err)
		}
		if err := os.WriteFile(configPath, yamlData, 0644); err != nil {
			rt.Fatalf("Failed to write config file: %v", err)
		}
		defer os.Remove(configPath)

		// Set only selected environment variables
		for _, tc := range testCases {
			if setEnvVars[tc.envName] {
				os.Setenv(tc.envName, envVarValues[tc.envName])
			} else {
				os.Unsetenv(tc.envName)
			}
		}
		defer func() {
			for _, tc := range testCases {
				os.Unsetenv(tc.envName)
			}
		}()

		// Load config
		loadedConfig, err := Load(configPath)
		if err != nil {
			rt.Fatalf("Failed to load config: %v", err)
		}

		// Verify correct values are used based on whether env var was set
		for _, tc := range testCases {
			actualValue := *tc.configPath(loadedConfig)
			var expectedValue string
			if setEnvVars[tc.envName] {
				expectedValue = envVarValues[tc.envName]
			} else {
				expectedValue = configFileValues[tc.envName]
			}

			if actualValue != expectedValue {
				if setEnvVars[tc.envName] {
					rt.Fatalf("Environment variable %s was set but did not override config file value.\n"+
						"Expected (env var): %s\n"+
						"Got: %s",
						tc.envName, expectedValue, actualValue)
				} else {
					rt.Fatalf("Environment variable %s was not set but config file value was not used.\n"+
						"Expected (config file): %s\n"+
						"Got: %s",
						tc.envName, expectedValue, actualValue)
				}
			}
		}
	})
}

// createMinimalConfig creates a minimal valid configuration for testing.
func createMinimalConfig() Config {
	return Config{
		Feishu: FeishuConfig{
			AppID:        "test-app-id",
			AppSecret:    "test-app-secret",
			ApprovalCode: "test-approval-code",
			ChatID:       "test-chat-id",
			BaseURL:      "https://open.feishu.cn",
		},
		Database: DatabaseConfig{
			Host:         "127.0.0.1",
			Port:         3306,
			User:         "test-user",
			Password:     "test-password",
			Database:     "security",
			MaxOpenConns: 10,
			MaxIdleConns: 5,
			MaxLifetime:  time.Hour,
		},
		Providers: ProvidersConfig{
			AWS: AWSConfig{
				IdentityCenterARN: "arn:aws:sso:::instance/test",
				IdentityStoreID:   "d-test",
				Region:            "ap-northeast-1",
				Accounts:          []CloudAccount{},
			},
			GCP: GCPConfig{
				CredentialsFile: "./creds.json",
				RoleMappingFile: "./roles.ini",
				Projects:        []CloudAccount{},
			},
			Azure: AzureConfig{
				TenantID:      "test-tenant-id",
				ClientID:      "test-client-id",
				ClientSecret:  "test-client-secret",
				Subscriptions: []CloudAccount{},
			},
			Alibaba: AlibabaConfig{
				RoleName:    "test-role",
				DirectoryID: "d-test",
				Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
				Accounts:    []CloudAccount{},
			},
		},
		Runtime: RuntimeConfig{
			PollInterval:        5 * time.Second,
			ExpireCheckInterval: time.Hour,
			GracePeriodDays:     3,
			HTTPTimeout:         10 * time.Second,
			MaxRetries:          3,
			LogLevel:            "info",
			WhitelistEmails:     []string{},
		},
		PermissionMappings: []PermissionMapping{},
		GCPRoleMappings:    []GCPRoleMapping{},
	}
}

// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// For any required configuration item, when that configuration item is missing,
// config validation should return a clear error message.
//
// **Validates: Requirements 2.9, 9.3, 9.8**

// RequiredFieldInfo describes a required configuration field for testing.
type RequiredFieldInfo struct {
	Name        string        // Human-readable field name
	FieldPath   string        // Field path for error matching (e.g., "feishu.app_id")
	ClearField  func(*Config) // Function to clear/empty the field
	NeedsSetup  func(*Config) // Optional setup function (e.g., add accounts to trigger validation)
	Description string        // Description of when this field is required
}

// getRequiredFields returns all required fields that should trigger validation errors when missing.
func getRequiredFields() []RequiredFieldInfo {
	return []RequiredFieldInfo{
		// Feishu required fields - always required
		{
			Name:        "Feishu AppID",
			FieldPath:   "feishu.app_id",
			ClearField:  func(c *Config) { c.Feishu.AppID = "" },
			Description: "Feishu AppID is always required",
		},
		{
			Name:        "Feishu AppSecret",
			FieldPath:   "feishu.app_secret",
			ClearField:  func(c *Config) { c.Feishu.AppSecret = "" },
			Description: "Feishu AppSecret is always required",
		},
		// Database required fields - always required
		{
			Name:        "Database User",
			FieldPath:   "database.user",
			ClearField:  func(c *Config) { c.Database.User = "" },
			Description: "Database User is always required",
		},
		{
			Name:        "Database Password",
			FieldPath:   "database.password",
			ClearField:  func(c *Config) { c.Database.Password = "" },
			Description: "Database Password is always required",
		},
		// AWS required fields - required when AWS accounts exist
		{
			Name:      "AWS IdentityCenterARN",
			FieldPath: "providers.aws.identity_center_arn",
			ClearField: func(c *Config) {
				c.Providers.AWS.IdentityCenterARN = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.AWS.Accounts = []CloudAccount{{ID: "123456789012", Name: "test-account", Type: "prod"}}
			},
			Description: "AWS IdentityCenterARN is required when AWS accounts are configured",
		},
		{
			Name:      "AWS IdentityStoreID",
			FieldPath: "providers.aws.identity_store_id",
			ClearField: func(c *Config) {
				c.Providers.AWS.IdentityStoreID = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.AWS.Accounts = []CloudAccount{{ID: "123456789012", Name: "test-account", Type: "prod"}}
			},
			Description: "AWS IdentityStoreID is required when AWS accounts are configured",
		},
		// GCP required fields - required when GCP projects exist
		{
			Name:      "GCP CredentialsFile",
			FieldPath: "providers.gcp.credentials_file",
			ClearField: func(c *Config) {
				c.Providers.GCP.CredentialsFile = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.GCP.Projects = []CloudAccount{{ID: "my-project", Name: "my-project", Type: "prod"}}
			},
			Description: "GCP CredentialsFile is required when GCP projects are configured",
		},
		// Azure required fields - required when Azure subscriptions exist
		{
			Name:      "Azure TenantID",
			FieldPath: "providers.azure.tenant_id",
			ClearField: func(c *Config) {
				c.Providers.Azure.TenantID = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test-sub", Type: "prod"}}
			},
			Description: "Azure TenantID is required when Azure subscriptions are configured",
		},
		{
			Name:      "Azure ClientID",
			FieldPath: "providers.azure.client_id",
			ClearField: func(c *Config) {
				c.Providers.Azure.ClientID = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test-sub", Type: "prod"}}
			},
			Description: "Azure ClientID is required when Azure subscriptions are configured",
		},
		{
			Name:      "Azure ClientSecret",
			FieldPath: "providers.azure.client_secret",
			ClearField: func(c *Config) {
				c.Providers.Azure.ClientSecret = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test-sub", Type: "prod"}}
			},
			Description: "Azure ClientSecret is required when Azure subscriptions are configured",
		},
		// Alibaba required fields - required when Alibaba accounts exist
		{
			Name:      "Alibaba DirectoryID",
			FieldPath: "providers.alibaba.directory_id",
			ClearField: func(c *Config) {
				c.Providers.Alibaba.DirectoryID = ""
			},
			NeedsSetup: func(c *Config) {
				c.Providers.Alibaba.Accounts = []CloudAccount{{ID: "1234567890123456", Name: "test-account", Type: "prod"}}
			},
			Description: "Alibaba DirectoryID is required when Alibaba accounts are configured",
		},
	}
}

// createValidConfigForValidation creates a valid configuration that passes validation.
// This is used as a baseline for testing missing required fields.
func createValidConfigForValidation() Config {
	return Config{
		Feishu: FeishuConfig{
			AppID:        "test-app-id",
			AppSecret:    "test-app-secret",
			ApprovalCode: "test-approval-code",
			ChatID:       "test-chat-id",
			BaseURL:      "https://open.feishu.cn",
		},
		Database: DatabaseConfig{
			Host:         "127.0.0.1",
			Port:         3306,
			User:         "test-user",
			Password:     "test-password",
			Database:     "security",
			MaxOpenConns: 10,
			MaxIdleConns: 5,
			MaxLifetime:  time.Hour,
		},
		Providers: ProvidersConfig{
			AWS: AWSConfig{
				IdentityCenterARN: "arn:aws:sso:::instance/ssoins-test123",
				IdentityStoreID:   "d-test123",
				Region:            "ap-northeast-1",
				Accounts:          []CloudAccount{},
			},
			GCP: GCPConfig{
				CredentialsFile: "./creds.json",
				RoleMappingFile: "./roles.ini",
				Projects:        []CloudAccount{},
			},
			Azure: AzureConfig{
				TenantID:      "12345678-1234-1234-1234-123456789012",
				ClientID:      "12345678-1234-1234-1234-123456789012",
				ClientSecret:  "test-client-secret",
				Subscriptions: []CloudAccount{},
			},
			Alibaba: AlibabaConfig{
				RoleName:    "test-role",
				DirectoryID: "d-00ts1z0rjv32",
				Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com", // Default value
				Accounts:    []CloudAccount{},
			},
		},
		Runtime: RuntimeConfig{
			PollInterval:        5 * time.Second,
			ExpireCheckInterval: time.Hour,
			GracePeriodDays:     3,
			HTTPTimeout:         10 * time.Second,
			MaxRetries:          3,
			LogLevel:            "info",
			WhitelistEmails:     []string{},
		},
		PermissionMappings: []PermissionMapping{},
		GCPRoleMappings:    []GCPRoleMapping{},
	}
}

// TestRequiredConfigMissingReturnsError tests that when any required configuration
// item is missing, validation returns a clear error message.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestRequiredConfigMissingReturnsError(t *testing.T) {
	requiredFields := getRequiredFields()

	rapid.Check(t, func(rt *rapid.T) {
		// Randomly select a required field to test
		fieldIndex := rapid.IntRange(0, len(requiredFields)-1).Draw(rt, "field_index")
		field := requiredFields[fieldIndex]

		// Create a valid config
		cfg := createValidConfigForValidation()

		// Apply setup if needed (e.g., add accounts to trigger conditional validation)
		if field.NeedsSetup != nil {
			field.NeedsSetup(&cfg)
		}

		// Clear the required field
		field.ClearField(&cfg)

		// Validate the config
		err := cfg.Validate()

		// Verify that validation returns an error
		if err == nil {
			rt.Fatalf("Expected validation error when %s is missing (%s), but got nil.\n"+
				"Field: %s\n"+
				"Description: %s",
				field.Name, field.FieldPath, field.FieldPath, field.Description)
		}

		// Verify that the error message contains the field path
		if !containsFieldError(err, field.FieldPath) {
			rt.Fatalf("Validation error does not mention the missing field.\n"+
				"Field: %s (%s)\n"+
				"Expected error to contain: %s\n"+
				"Actual error: %v",
				field.Name, field.Description, field.FieldPath, err)
		}
	})
}

// TestRequiredConfigMissingReturnsErrorForEachField tests each required field
// individually to ensure validation returns an error when it's missing.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestRequiredConfigMissingReturnsErrorForEachField(t *testing.T) {
	requiredFields := getRequiredFields()

	for _, field := range requiredFields {
		field := field // capture range variable
		t.Run(field.Name, func(t *testing.T) {
			rapid.Check(t, func(rt *rapid.T) {
				// Create a valid config
				cfg := createValidConfigForValidation()

				// Apply setup if needed
				if field.NeedsSetup != nil {
					field.NeedsSetup(&cfg)
				}

				// Clear the required field
				field.ClearField(&cfg)

				// Validate the config
				err := cfg.Validate()

				// Verify that validation returns an error
				if err == nil {
					rt.Fatalf("Expected validation error when %s is missing, but got nil.\n"+
						"Description: %s",
						field.Name, field.Description)
				}

				// Verify that the error message contains the field path
				if !containsFieldError(err, field.FieldPath) {
					rt.Fatalf("Validation error does not mention the missing field.\n"+
						"Field: %s\n"+
						"Expected error to contain: %s\n"+
						"Actual error: %v",
						field.Name, field.FieldPath, err)
				}
			})
		})
	}
}

// TestMultipleRequiredConfigsMissing tests that when multiple required fields
// are missing, validation returns errors for all of them.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestMultipleRequiredConfigsMissing(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Create a valid config
		cfg := createValidConfigForValidation()

		// Randomly select which required fields to clear (at least 2)
		requiredFields := getRequiredFields()
		numFieldsToClear := rapid.IntRange(2, len(requiredFields)).Draw(rt, "num_fields")

		// Shuffle and select fields
		selectedIndices := make([]int, len(requiredFields))
		for i := range selectedIndices {
			selectedIndices[i] = i
		}
		// Fisher-Yates shuffle
		for i := len(selectedIndices) - 1; i > 0; i-- {
			j := rapid.IntRange(0, i).Draw(rt, "shuffle_index")
			selectedIndices[i], selectedIndices[j] = selectedIndices[j], selectedIndices[i]
		}

		// Clear selected fields and track which ones we cleared
		clearedFields := make([]RequiredFieldInfo, 0, numFieldsToClear)
		for i := 0; i < numFieldsToClear; i++ {
			field := requiredFields[selectedIndices[i]]

			// Apply setup if needed
			if field.NeedsSetup != nil {
				field.NeedsSetup(&cfg)
			}

			// Clear the field
			field.ClearField(&cfg)
			clearedFields = append(clearedFields, field)
		}

		// Validate the config
		err := cfg.Validate()

		// Verify that validation returns an error
		if err == nil {
			fieldNames := make([]string, len(clearedFields))
			for i, f := range clearedFields {
				fieldNames[i] = f.Name
			}
			rt.Fatalf("Expected validation error when multiple fields are missing, but got nil.\n"+
				"Missing fields: %v", fieldNames)
		}

		// Verify that the error is a ValidationErrors type (multiple errors)
		if len(clearedFields) > 1 {
			// The error should mention at least one of the cleared fields
			foundAny := false
			for _, field := range clearedFields {
				if containsFieldError(err, field.FieldPath) {
					foundAny = true
					break
				}
			}
			if !foundAny {
				fieldPaths := make([]string, len(clearedFields))
				for i, f := range clearedFields {
					fieldPaths[i] = f.FieldPath
				}
				rt.Fatalf("Validation error does not mention any of the missing fields.\n"+
					"Expected one of: %v\n"+
					"Actual error: %v",
					fieldPaths, err)
			}
		}
	})
}

// TestValidConfigPassesValidation tests that a valid configuration passes validation.
// This is a sanity check to ensure our test setup is correct.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestValidConfigPassesValidation(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Create a valid config
		cfg := createValidConfigForValidation()

		// Validate the config
		err := cfg.Validate()

		// Verify that validation passes
		if err != nil {
			rt.Fatalf("Expected valid config to pass validation, but got error: %v", err)
		}
	})
}

// TestConditionalRequiredFieldsNotRequiredWhenEmpty tests that conditional required
// fields (like AWS IdentityCenterARN) are not required when their associated
// resources (like AWS accounts) are empty.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestConditionalRequiredFieldsNotRequiredWhenEmpty(t *testing.T) {
	rapid.Check(t, func(rt *rapid.T) {
		// Create a config with empty cloud provider accounts
		cfg := Config{
			Feishu: FeishuConfig{
				AppID:        genNonEmptyString().Draw(rt, "app_id"),
				AppSecret:    genNonEmptyString().Draw(rt, "app_secret"),
				ApprovalCode: genNonEmptyString().Draw(rt, "approval_code"),
				ChatID:       genNonEmptyString().Draw(rt, "chat_id"),
				BaseURL:      "https://open.feishu.cn",
			},
			Database: DatabaseConfig{
				Host:         "127.0.0.1",
				Port:         3306,
				User:         genNonEmptyString().Draw(rt, "db_user"),
				Password:     genNonEmptyString().Draw(rt, "db_password"),
				Database:     "security",
				MaxOpenConns: 10,
				MaxIdleConns: 5,
				MaxLifetime:  time.Hour,
			},
			Providers: ProvidersConfig{
				// AWS with empty accounts - IdentityCenterARN and IdentityStoreID should NOT be required
				AWS: AWSConfig{
					IdentityCenterARN: "", // Empty, but should be OK since no accounts
					IdentityStoreID:   "", // Empty, but should be OK since no accounts
					Region:            "ap-northeast-1",
					Accounts:          []CloudAccount{}, // Empty!
				},
				// GCP with empty projects - CredentialsFile should NOT be required
				GCP: GCPConfig{
					CredentialsFile: "", // Empty, but should be OK since no projects
					RoleMappingFile: "",
					Projects:        []CloudAccount{}, // Empty!
				},
				// Azure with empty subscriptions - credentials should NOT be required
				Azure: AzureConfig{
					TenantID:      "",               // Empty, but should be OK since no subscriptions
					ClientID:      "",               // Empty, but should be OK since no subscriptions
					ClientSecret:  "",               // Empty, but should be OK since no subscriptions
					Subscriptions: []CloudAccount{}, // Empty!
				},
				// Alibaba with empty accounts - credentials should NOT be required
				Alibaba: AlibabaConfig{
					DirectoryID:     "", // Empty, but should be OK since no accounts
					Endpoint:        "cloudsso.cn-shanghai.aliyuncs.com",
					Accounts:        []CloudAccount{}, // Empty!
				},
			},
			Runtime: RuntimeConfig{
				PollInterval:        5 * time.Second,
				ExpireCheckInterval: time.Hour,
				GracePeriodDays:     3,
				HTTPTimeout:         10 * time.Second,
				MaxRetries:          3,
				LogLevel:            "info",
				WhitelistEmails:     []string{},
			},
			PermissionMappings: []PermissionMapping{},
			GCPRoleMappings:    []GCPRoleMapping{},
		}

		// Validate the config
		err := cfg.Validate()

		// Verify that validation passes (conditional fields not required when accounts are empty)
		if err != nil {
			rt.Fatalf("Expected config with empty cloud accounts to pass validation "+
				"(conditional required fields should not be required), but got error: %v", err)
		}
	})
}

// TestConditionalRequiredFieldsRequiredWhenAccountsExist tests that conditional
// required fields ARE required when their associated resources exist.
//
// Feature: autosso-refactoring, Property 3: 必需配置缺失时返回错误
// **Validates: Requirements 2.9, 9.3, 9.8**
func TestConditionalRequiredFieldsRequiredWhenAccountsExist(t *testing.T) {
	testCases := []struct {
		name      string
		setupFunc func(*Config)
		clearFunc func(*Config)
		fieldPath string
	}{
		{
			name: "AWS_IdentityCenterARN_required_with_accounts",
			setupFunc: func(c *Config) {
				c.Providers.AWS.Accounts = []CloudAccount{{ID: "123456789012", Name: "test", Type: "prod"}}
				c.Providers.AWS.IdentityStoreID = "d-test123"
			},
			clearFunc: func(c *Config) {
				c.Providers.AWS.IdentityCenterARN = ""
			},
			fieldPath: "providers.aws.identity_center_arn",
		},
		{
			name: "AWS_IdentityStoreID_required_with_accounts",
			setupFunc: func(c *Config) {
				c.Providers.AWS.Accounts = []CloudAccount{{ID: "123456789012", Name: "test", Type: "prod"}}
				c.Providers.AWS.IdentityCenterARN = "arn:aws:sso:::instance/ssoins-test"
			},
			clearFunc: func(c *Config) {
				c.Providers.AWS.IdentityStoreID = ""
			},
			fieldPath: "providers.aws.identity_store_id",
		},
		{
			name: "GCP_CredentialsFile_required_with_projects",
			setupFunc: func(c *Config) {
				c.Providers.GCP.Projects = []CloudAccount{{ID: "my-project", Name: "my-project", Type: "prod"}}
			},
			clearFunc: func(c *Config) {
				c.Providers.GCP.CredentialsFile = ""
			},
			fieldPath: "providers.gcp.credentials_file",
		},
		{
			name: "Azure_TenantID_required_with_subscriptions",
			setupFunc: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test", Type: "prod"}}
				c.Providers.Azure.ClientID = "12345678-1234-1234-1234-123456789012"
				c.Providers.Azure.ClientSecret = "test-secret"
			},
			clearFunc: func(c *Config) {
				c.Providers.Azure.TenantID = ""
			},
			fieldPath: "providers.azure.tenant_id",
		},
		{
			name: "Azure_ClientID_required_with_subscriptions",
			setupFunc: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test", Type: "prod"}}
				c.Providers.Azure.TenantID = "12345678-1234-1234-1234-123456789012"
				c.Providers.Azure.ClientSecret = "test-secret"
			},
			clearFunc: func(c *Config) {
				c.Providers.Azure.ClientID = ""
			},
			fieldPath: "providers.azure.client_id",
		},
		{
			name: "Azure_ClientSecret_required_with_subscriptions",
			setupFunc: func(c *Config) {
				c.Providers.Azure.Subscriptions = []CloudAccount{{ID: "12345678-1234-1234-1234-123456789012", Name: "test", Type: "prod"}}
				c.Providers.Azure.TenantID = "12345678-1234-1234-1234-123456789012"
				c.Providers.Azure.ClientID = "12345678-1234-1234-1234-123456789012"
			},
			clearFunc: func(c *Config) {
				c.Providers.Azure.ClientSecret = ""
			},
			fieldPath: "providers.azure.client_secret",
		},

		{
			name: "Alibaba_DirectoryID_required_with_accounts",
			setupFunc: func(c *Config) {
				c.Providers.Alibaba.Accounts = []CloudAccount{{ID: "1234567890123456", Name: "test", Type: "prod"}}
			},
			clearFunc: func(c *Config) {
				c.Providers.Alibaba.DirectoryID = ""
			},
			fieldPath: "providers.alibaba.directory_id",
		},
	}

	for _, tc := range testCases {
		tc := tc // capture range variable
		t.Run(tc.name, func(t *testing.T) {
			rapid.Check(t, func(rt *rapid.T) {
				// Create a valid base config
				cfg := createValidConfigForValidation()

				// Apply setup (add accounts/projects/subscriptions)
				tc.setupFunc(&cfg)

				// Clear the required field
				tc.clearFunc(&cfg)

				// Validate the config
				err := cfg.Validate()

				// Verify that validation returns an error
				if err == nil {
					rt.Fatalf("Expected validation error when %s is missing with accounts configured, but got nil",
						tc.fieldPath)
				}

				// Verify that the error message contains the field path
				if !containsFieldError(err, tc.fieldPath) {
					rt.Fatalf("Validation error does not mention the missing field.\n"+
						"Expected error to contain: %s\n"+
						"Actual error: %v",
						tc.fieldPath, err)
				}
			})
		})
	}
}
