package config

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"veza-backend-api/internal/database"
	"veza-backend-api/internal/email"
	"veza-backend-api/internal/eventbus" // Import the eventbus package
	"veza-backend-api/internal/logging"
	"veza-backend-api/internal/metrics"
	"veza-backend-api/internal/middleware"
	"veza-backend-api/internal/repositories"
	"veza-backend-api/internal/services"
	"veza-backend-api/internal/workers"

	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

// Config contient toute la configuration de l'application
type Config struct {
	// Base de données
	Database *database.Database

	// Redis
	RedisClient *redis.Client

	// Services
	SessionService    *services.SessionService
	AuditService      *services.AuditService
	TOTPService       *services.TOTPService
	UploadValidator   *services.UploadValidator
	CacheService      *services.CacheService
	PlaylistService   *services.PlaylistService
	PermissionService *services.PermissionService
	JWTService        *services.JWTService
	UserService       *services.UserService
	S3StorageService  *services.S3StorageService // BE-SVC-005: S3 storage service

	// Middlewares
	RateLimiter       *middleware.RateLimiter
	SimpleRateLimiter *middleware.SimpleRateLimiter // Rate limiter simple (T0015)
	EndpointLimiter   *middleware.EndpointLimiter
	UserRateLimiter   *middleware.UserRateLimiter // BE-SVC-002: Per-user rate limiting
	AuthMiddleware    *middleware.AuthMiddleware

	// Logger
	Logger *zap.Logger

	// Metrics (T0020)
	ErrorMetrics *metrics.ErrorMetrics

	// Secrets Provider (T0037)
	SecretsProvider SecretsProvider

	// Config Watcher (T0040)
	ConfigWatcher *ConfigWatcher

	// Configuration
	Env             string // Environnement: development, test, production (P0-SECURITY)
	AppPort         int    // Port pour le serveur HTTP (T0031)
	JWTSecret       string
	JWTIssuer       string // T0204: Issuer claim validation (P1-SECURITY)
	JWTAudience     string // T0204: Audience claim validation (P1-SECURITY)
	ChatJWTSecret   string // Secret pour les tokens WebSocket Chat
	RedisURL        string
	RedisEnable     bool // Enable/Disable Redis
	DatabaseURL     string
	UploadDir       string   // Répertoire d'upload
	StreamServerURL string   // URL du serveur de streaming
	ChatServerURL   string   // URL du serveur de chat
	CORSOrigins     []string // Liste des origines CORS autorisées

	// S3 Storage Configuration (BE-SVC-005)
	S3Bucket    string // Nom du bucket S3
	S3Region    string // Région AWS
	S3Endpoint  string // Endpoint personnalisé (pour MinIO, etc.)
	S3AccessKey string // Access key AWS (optionnel, utilise les credentials par défaut si vide)
	S3SecretKey string // Secret key AWS (optionnel, utilise les credentials par défaut si vide)
	S3Enabled   bool   // Activer le stockage S3

	// Sentry configuration
	SentryDsn                    string        // DSN Sentry pour error tracking
	SentryEnvironment            string        // Environnement Sentry (dev, staging, prod)
	SentrySampleRateErrors       float64       // Sample rate pour les erreurs (0.0-1.0)
	SentrySampleRateTransactions float64       // Sample rate pour les transactions (0.0-1.0)
	RateLimitLimit               int           // Limite de requêtes pour le rate limiter simple
	RateLimitWindow              int           // Fenêtre de temps en secondes pour le rate limiter simple
	AuthRateLimitLoginAttempts   int           // Max login attempts (PR-3)
	AuthRateLimitLoginWindow     int           // Login rate limit window in minutes (PR-3)
	HandlerTimeout               time.Duration // Global handler timeout (PR-6)
	LogLevel                     string        // Niveau de log (T0027)
	DBMaxRetries                 int
	DBRetryInterval              time.Duration
	MaxConcurrentUploads         int // MOD-P2-005: Limite uploads simultanés (backpressure)

	// Log Aggregation (BE-SVC-015)
	LogAggregationEnabled       bool              // Activer l'agrégation de logs
	LogAggregationEndpoint      string            // URL du service d'agrégation (ex: "http://loki:3100/loki/api/v1/push")
	LogAggregationBatchSize     int               // Nombre de logs à accumuler avant envoi
	LogAggregationFlushInterval time.Duration     // Intervalle de flush automatique
	LogAggregationTimeout       time.Duration     // Timeout pour les requêtes HTTP
	LogAggregationLabels        map[string]string // Labels statiques pour les logs

	// Log Files Configuration
	LogDir string // Répertoire pour les fichiers de logs (ex: "/var/log/veza")

	// RabbitMQ
	RabbitMQEventBus      *eventbus.RabbitMQEventBus // Ajout de l'instance de l'EventBus
	RabbitMQURL           string
	RabbitMQMaxRetries    int
	RabbitMQRetryInterval time.Duration
	RabbitMQEnable        bool

	// Cookie Security Settings
	CookieSecure   bool   // Secure flag (true en production, false en dev)
	CookieSameSite string // SameSite policy: strict, lax, none
	CookieDomain   string // Cookie domain (vide pour domaine actuel)
	CookieHttpOnly bool   // HttpOnly flag (toujours true pour refresh_token)
	CookiePath     string // Cookie path (généralement "/")

	// Email & Jobs
	EmailSender *email.SMTPEmailSender
	JobWorker   *workers.JobWorker
	SMTPConfig  email.SMTPConfig
}

// INT-019: ValidateRequiredEnvironmentVariables valide toutes les variables d'environnement requises
// Cette fonction vérifie que toutes les variables critiques sont définies avant le chargement de la configuration
func ValidateRequiredEnvironmentVariables(env string) error {
	var missingVars []string
	var errors []string

	// Variables requises dans tous les environnements
	requiredVars := []string{
		"JWT_SECRET",
		"DATABASE_URL",
	}

	// Vérifier les variables requises
	for _, varName := range requiredVars {
		value := os.Getenv(varName)
		if value == "" {
			missingVars = append(missingVars, varName)
		}
	}

	// Validation spécifique selon l'environnement
	if env == EnvProduction {
		// En production, CORS_ALLOWED_ORIGINS est requis
		corsOrigins := os.Getenv("CORS_ALLOWED_ORIGINS")
		if corsOrigins == "" {
			missingVars = append(missingVars, "CORS_ALLOWED_ORIGINS")
		} else {
			// Vérifier qu'il n'y a pas de wildcard en production
			if strings.Contains(corsOrigins, "*") {
				errors = append(errors, "CORS_ALLOWED_ORIGINS cannot contain wildcard '*' in production environment")
			}
		}

		// En production, vérifier que RabbitMQ URL est défini si RabbitMQ est activé
		rabbitMQEnable := os.Getenv("RABBITMQ_ENABLE")
		if rabbitMQEnable != "false" {
			rabbitMQURL := os.Getenv("RABBITMQ_URL")
			if rabbitMQURL == "" {
				errors = append(errors, "RABBITMQ_URL is required in production when RabbitMQ is enabled")
			}
		}

		// En production, LOG_LEVEL ne doit pas être DEBUG
		logLevel := os.Getenv("LOG_LEVEL")
		if logLevel == "DEBUG" {
			errors = append(errors, "LOG_LEVEL=DEBUG is not allowed in production environment for security reasons")
		}
	}

	// Construire le message d'erreur
	if len(missingVars) > 0 {
		errors = append(errors, fmt.Sprintf("required environment variables are missing: %v", missingVars))
	}

	if len(errors) > 0 {
		return fmt.Errorf("environment variable validation failed: %s", strings.Join(errors, "; "))
	}

	return nil
}

// NewConfig crée une nouvelle configuration
func NewConfig() (*Config, error) {
	// Déterminer l'environnement avec détection automatique améliorée (T0032, T0039)
	env := DetectEnvironment()

	// INT-019: Valider les variables d'environnement requises avant de charger la configuration
	if err := ValidateRequiredEnvironmentVariables(env); err != nil {
		return nil, fmt.Errorf("environment validation failed: %w", err)
	}

	// Charger les fichiers .env selon l'environnement (T0032)
	// Charge dans l'ordre: .env.{env}, .env
	// Les variables d'environnement système ont priorité
	if err := LoadEnvFiles(env); err != nil {
		// En cas d'erreur, continuer quand même (peut-être que les fichiers .env n'existent pas)
		// Les variables d'environnement système seront utilisées
	}

	// FIX #2: Charger LOG_LEVEL AVANT d'initialiser le logger
	// Charger le niveau de log depuis les variables d'environnement (T0027)
	// Valeurs possibles: DEBUG, INFO, WARN, ERROR
	// Par défaut: INFO
	logLevel := getEnv("LOG_LEVEL", "INFO")

	// SECURITY: Charger les origines CORS avec defaults sécurisés selon l'environnement (P0-SECURITY)
	corsOrigins := getCORSOrigins(env)

	// Charger la configuration du rate limiter simple
	// En développement, augmenter les limites pour éviter les erreurs lors des tests
	rateLimitLimit := getEnvInt("RATE_LIMIT_LIMIT", 200)  // Augmenté de 100 à 200 en dev
	rateLimitWindow := getEnvInt("RATE_LIMIT_WINDOW", 60) // 60 secondes (1 minute) par défaut

	// Charger le port depuis les variables d'environnement (T0031)
	appPort := getEnvInt("APP_PORT", 8080)

	// MOD-P2-005: Charger la limite d'uploads simultanés (backpressure)
	maxConcurrentUploads := getEnvInt("MAX_CONCURRENT_UPLOADS", 10) // 10 par défaut

	// Configuration depuis les variables d'environnement
	// SECURITY: JWT_SECRET est REQUIS - pas de valeur par défaut pour éviter les failles de sécurité
	jwtSecret, err := getEnvRequired("JWT_SECRET")
	if err != nil {
		return nil, err
	}

	databaseURL, err := getEnvRequired("DATABASE_URL")
	if err != nil {
		return nil, err
	}

	// BE-SEC-014: Get RabbitMQ URL with environment-aware defaults
	rabbitMQURL := getRabbitMQURL(env)

	config := &Config{
		Env:           env, // Store environment for validation (P0-SECURITY)
		AppPort:       appPort,
		JWTSecret:     jwtSecret,
		JWTIssuer:     getEnv("JWT_ISSUER", "veza-api"),
		JWTAudience:   getEnv("JWT_AUDIENCE", "veza-app"),
		ChatJWTSecret: getEnv("CHAT_JWT_SECRET", jwtSecret), // Fallback to main JWT secret if not set
		RedisURL:      getEnv("REDIS_URL", "redis://localhost:6379"),
		RedisEnable:   getEnvBool("REDIS_ENABLE", true),
		// SECURITY: DATABASE_URL est REQUIS - contient des credentials sensibles
		DatabaseURL:     databaseURL,
		UploadDir:       getEnv("UPLOAD_DIR", "uploads"),
		StreamServerURL: getEnv("STREAM_SERVER_URL", "http://localhost:8082"),
		ChatServerURL:   getEnv("CHAT_SERVER_URL", "http://localhost:8081"),
		CORSOrigins:     corsOrigins,

		// S3 Storage Configuration (BE-SVC-005)
		S3Bucket:    getEnv("AWS_S3_BUCKET", ""),
		S3Region:    getEnv("AWS_REGION", "us-east-1"),
		S3Endpoint:  getEnv("AWS_S3_ENDPOINT", ""), // Optionnel, pour MinIO
		S3AccessKey: getEnv("AWS_ACCESS_KEY_ID", ""),
		S3SecretKey: getEnv("AWS_SECRET_ACCESS_KEY", ""),
		S3Enabled:   getEnvBool("AWS_S3_ENABLED", false), // Désactivé par défaut

		// Sentry configuration
		SentryDsn:                    getEnv("SENTRY_DSN", ""),
		SentryEnvironment:            env, // Utiliser l'environnement détecté
		SentrySampleRateErrors:       getEnvFloat64("SENTRY_SAMPLE_RATE_ERRORS", 1.0),
		SentrySampleRateTransactions: getEnvFloat64("SENTRY_SAMPLE_RATE_TRANSACTIONS", 0.1),
		RateLimitLimit:               rateLimitLimit,
		RateLimitWindow:              rateLimitWindow,
		// Augmenter les limites pour l'environnement de test/E2E
		AuthRateLimitLoginAttempts: getAuthRateLimitLoginAttempts(env),
		AuthRateLimitLoginWindow:   getAuthRateLimitLoginWindow(env),
		HandlerTimeout:             getEnvDuration("HANDLER_TIMEOUT", 30*time.Second), // Default: 30 seconds
		LogLevel:                   logLevel,
		Logger:                     nil,                                                // Sera initialisé après selon LOG_LEVEL et agrégation
		DBMaxRetries:               getEnvInt("DB_MAX_RETRIES", 5),                     // 5 tentatives par défaut
		DBRetryInterval:            getEnvDuration("DB_RETRY_INTERVAL", 5*time.Second), // 5 secondes par défaut
		MaxConcurrentUploads:       maxConcurrentUploads,                               // MOD-P2-005: Limite uploads simultanés

		// Log Aggregation Configuration (BE-SVC-015)
		// FIX #26: Activer l'agrégation par défaut en production si l'endpoint est configuré
		LogAggregationEndpoint:      getEnv("LOG_AGGREGATION_ENDPOINT", ""),                          // Ex: "http://loki:3100/loki/api/v1/push"
		LogAggregationBatchSize:     getEnvInt("LOG_AGGREGATION_BATCH_SIZE", 100),                    // 100 logs par batch
		LogAggregationFlushInterval: getEnvDuration("LOG_AGGREGATION_FLUSH_INTERVAL", 5*time.Second), // Flush toutes les 5 secondes
		LogAggregationTimeout:       getEnvDuration("LOG_AGGREGATION_TIMEOUT", 10*time.Second),       // Timeout de 10 secondes
		LogAggregationLabels:        parseLogAggregationLabels(getEnv("LOG_AGGREGATION_LABELS", "")), // Labels au format "key1=value1,key2=value2"

		// Configuration RabbitMQ
		// BE-SEC-014: In production, require RABBITMQ_URL to be set (no default with credentials)
		RabbitMQURL:           rabbitMQURL,
		RabbitMQMaxRetries:    getEnvInt("RABBITMQ_MAX_RETRIES", 3),                     // 3 tentatives par défaut
		RabbitMQRetryInterval: getEnvDuration("RABBITMQ_RETRY_INTERVAL", 2*time.Second), // 2 secondes par défaut
		RabbitMQEnable:        getEnvBool("RABBITMQ_ENABLE", true),                      // Activé par défaut

		// Cookie Security Configuration
		CookieSecure:   getCookieSecure(env),
		CookieSameSite: getCookieSameSite(env),
		CookieDomain:   getEnv("COOKIE_DOMAIN", ""),
		CookieHttpOnly: getEnvBool("COOKIE_HTTP_ONLY", true),
		CookiePath:     getEnv("COOKIE_PATH", "/"),

		// Log Files Configuration
		// En développement, utiliser ./logs si /var/log n'est pas accessible
		LogDir: func() string {
			logDir := getEnv("LOG_DIR", "/var/log/veza")
			// En développement, préférer un répertoire local si /var/log n'est pas accessible
			if env == EnvDevelopment || env == "dev" {
				if logDir == "/var/log/veza" {
					// Essayer de créer le répertoire pour vérifier les permissions
					if err := os.MkdirAll("/var/log/veza", 0755); err != nil {
						// Si échec, utiliser ./logs
						return "./logs"
					}
					// Vérifier qu'on peut écrire dedans en créant un fichier test
					testFile := "/var/log/veza/.test_write"
					if f, err := os.Create(testFile); err != nil {
						// Ne peut pas écrire, utiliser ./logs
						return "./logs"
					} else {
						f.Close()
						os.Remove(testFile)
					}
				}
			}
			return logDir
		}(),
	}

	// Initialiser le SecretsProvider (T0037)
	secretKeys := DefaultSecretKeys()
	config.SecretsProvider = NewEnvSecretsProvider(secretKeys)

	// FIX #26: Activer l'agrégation par défaut en production si l'endpoint est configuré
	// Si LOG_AGGREGATION_ENABLED est explicitement défini, l'utiliser
	// Sinon, activer automatiquement en production si l'endpoint est configuré
	logAggregationEndpoint := config.LogAggregationEndpoint
	explicitlyEnabled := os.Getenv("LOG_AGGREGATION_ENABLED") != ""
	var logAggregationEnabled bool
	if explicitlyEnabled {
		// Si explicitement défini, respecter la valeur
		logAggregationEnabled = getEnvBool("LOG_AGGREGATION_ENABLED", false)
	} else {
		// Sinon, activer par défaut en production si l'endpoint est configuré
		logAggregationEnabled = (env == EnvProduction || env == EnvStaging) && logAggregationEndpoint != ""
	}
	config.LogAggregationEnabled = logAggregationEnabled

	// FIX #2: Initialiser le logger avec le bon niveau (LOG_LEVEL respecté)
	// BE-SVC-015: Utiliser logger avec agrégation si activée, sinon logger standard
	var logger *zap.Logger
	if config.LogAggregationEnabled && config.LogAggregationEndpoint != "" {
		aggConfig := &logging.AggregationConfig{
			EndpointURL:   config.LogAggregationEndpoint,
			Enabled:       true,
			BatchSize:     config.LogAggregationBatchSize,
			FlushInterval: config.LogAggregationFlushInterval,
			Timeout:       config.LogAggregationTimeout,
			Labels:        config.LogAggregationLabels,
		}
		// Ajouter des labels par défaut si non définis
		if aggConfig.Labels == nil {
			aggConfig.Labels = make(map[string]string)
		}
		if _, exists := aggConfig.Labels["service"]; !exists {
			aggConfig.Labels["service"] = "veza-api"
		}
		if _, exists := aggConfig.Labels["env"]; !exists {
			aggConfig.Labels["env"] = env
		}

		aggLogger, err := logging.NewLoggerWithAggregation(env, logLevel, aggConfig)
		if err != nil {
			// FIX #27: Fallback vers logger optimisé (asynchrone) si agrégation échoue
			// En production/staging, utiliser logger optimisé pour performance
			var stdLogger *logging.Logger
			var err2 error
			if env == EnvProduction || env == EnvStaging {
				stdLogger, err2 = logging.NewOptimizedLogger(env, logLevel)
				if err2 != nil {
					return nil, fmt.Errorf("failed to initialize optimized logger: %w", err2)
				}
			} else {
				stdLogger, err2 = logging.NewLogger(env, logLevel)
				if err2 != nil {
					return nil, fmt.Errorf("failed to initialize logger: %w", err2)
				}
			}
			logger = stdLogger.GetZapLogger()
			logger.Warn("Failed to initialize logger with aggregation, using optimized logger",
				zap.Error(err),
				zap.String("endpoint", config.LogAggregationEndpoint),
			)
		} else {
			logger = aggLogger.GetZapLogger()
			logger.Info("Logger with aggregation initialized",
				zap.String("endpoint", config.LogAggregationEndpoint),
				zap.Int("batch_size", config.LogAggregationBatchSize),
				zap.String("log_level", logLevel),
			)
		}
	} else {
		// Utiliser logger avec fichiers de rotation vers /var/log/veza/
		// Crée deux fichiers : backend-api.log (tous les logs) et backend-api-error.log (erreurs uniquement)
		stdLogger, err := logging.NewLoggerWithFileRotation(config.LogDir, "backend-api", env, logLevel)
		if err != nil {
			return nil, fmt.Errorf("failed to initialize logger with file rotation: %w", err)
		}
		logger = stdLogger.GetZapLogger()
		// Log initialization message - ignore any broken pipe errors silently
		// This is the first log message after logger initialization and may trigger broken pipe
		// if systemd journald is not ready or stdout/stderr is redirected incorrectly
		func() {
			defer func() {
				if r := recover(); r != nil {
					// Silently ignore panics from logger (shouldn't happen, but be safe)
					_ = r
				}
			}()
			logger.Info("Logger initialized with file rotation",
				zap.String("log_level", logLevel),
				zap.String("env", env),
				zap.String("log_dir", config.LogDir),
				zap.String("all_logs_file", fmt.Sprintf("%s/backend-api.log", config.LogDir)),
				zap.String("error_logs_file", fmt.Sprintf("%s/backend-api-error.log", config.LogDir)),
			)
		}()
	}

	// FIX #30: Appliquer le filtre de secrets au logger
	logger = logging.WrapLoggerWithSecretFilter(logger)

	// Assigner le logger à la config
	config.Logger = logger

	// SECURITY: Valider la configuration selon l'environnement (P0-SECURITY)
	if err := config.ValidateForEnvironment(); err != nil {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Error("Configuration validation failed", zap.Error(err), zap.String("env", env))
		}()
		return nil, fmt.Errorf("invalid configuration: %w", err)
	}

	// Warn if CORS is strict/empty in production (MOD-P0-002)
	if env == EnvProduction && len(config.CORSOrigins) == 0 {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Warn("CORS_ALLOWED_ORIGINS is empty in production. Strict mode enabled: ALL CORS requests will be rejected.")
		}()
	}

	// Créer des loggers séparés pour chaque module
	redisLoggerWrapper, err := logging.NewLoggerWithFileRotation(config.LogDir, "redis", env, logLevel)
	var redisLoggerZap *zap.Logger
	if err != nil {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Warn("Failed to create Redis logger, using main logger", zap.Error(err))
		}()
		redisLoggerZap = logger
	} else {
		redisLoggerZap = logging.WrapLoggerWithSecretFilter(redisLoggerWrapper.GetZapLogger())
	}

	dbLoggerWrapper, err := logging.NewLoggerWithFileRotation(config.LogDir, "db", env, logLevel)
	var dbLoggerZap *zap.Logger
	if err != nil {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Warn("Failed to create DB logger, using main logger", zap.Error(err))
		}()
		dbLoggerZap = logger
	} else {
		dbLoggerZap = logging.WrapLoggerWithSecretFilter(dbLoggerWrapper.GetZapLogger())
	}

	rabbitmqLoggerWrapper, err := logging.NewLoggerWithFileRotation(config.LogDir, "rabbitmq", env, logLevel)
	var rabbitmqLoggerZap *zap.Logger
	if err != nil {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Warn("Failed to create RabbitMQ logger, using main logger", zap.Error(err))
		}()
		rabbitmqLoggerZap = logger
	} else {
		rabbitmqLoggerZap = logging.WrapLoggerWithSecretFilter(rabbitmqLoggerWrapper.GetZapLogger())
	}

	// Initialiser Redis
	if config.RedisEnable {
		config.RedisClient, err = initRedis(config.RedisURL, redisLoggerZap)
		if err != nil {
			// CRITICAL: Protect logger calls from broken pipe errors
			func() {
				defer func() {
					if r := recover(); r != nil {
						_ = r
					}
				}()
				logger.Error("Failed to initialize Redis", zap.Error(err))
			}()
			return nil, err
		}
	} else {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Warn("Redis désactivé par configuration (REDIS_ENABLE=false)")
		}()
	}

	// Initialiser la base de données avec retry
	config.Database, err = initDatabaseWithRetry(config.DatabaseURL, config.DBMaxRetries, config.DBRetryInterval, dbLoggerZap)
	if err != nil {
		// CRITICAL: Protect logger calls from broken pipe errors
		func() {
			defer func() {
				if r := recover(); r != nil {
					_ = r
				}
			}()
			logger.Error("Failed to initialize database", zap.Error(err))
		}()
		return nil, fmt.Errorf("failed to initialize database: %w", err)
	}

	// Initialiser RabbitMQ avec retry
	config.RabbitMQEventBus, err = eventbus.NewRabbitMQEventBusWithRetry(&eventbus.RabbitMQConfig{
		URL:           config.RabbitMQURL,
		MaxRetries:    config.RabbitMQMaxRetries,
		RetryInterval: config.RabbitMQRetryInterval,
		Enable:        config.RabbitMQEnable,
	}, rabbitmqLoggerZap)
	if err != nil {
		// En mode dégradé, l'erreur n'est pas fatale au démarrage du service
		if _, ok := err.(*eventbus.EventBusUnavailableError); ok && !config.RabbitMQEnable {
			logger.Warn("RabbitMQ EventBus est indisponible mais le service démarre en mode dégradé.", zap.Error(err))
		} else if _, ok := err.(*eventbus.EventBusUnavailableError); ok {
			// Si le service est censé être enabled et qu'il est injoignable après retries
			logger.Fatal("Impossible de se connecter à RabbitMQ après plusieurs tentatives. Le service ne peut pas démarrer.", zap.Error(err))
			return nil, err // Retourner l'erreur fatale
		} else {
			logger.Error("Failed to initialize RabbitMQ EventBus", zap.Error(err))
			return nil, err
		}
	}

	// BE-SVC-005: Initialiser le service S3 si activé
	if config.S3Enabled && config.S3Bucket != "" {
		s3Service, err := services.NewS3StorageService(services.S3Config{
			Bucket:    config.S3Bucket,
			Region:    config.S3Region,
			Endpoint:  config.S3Endpoint,
			AccessKey: config.S3AccessKey,
			SecretKey: config.S3SecretKey,
			Logger:    logger,
		})
		if err != nil {
			logger.Warn("Failed to initialize S3 storage service, falling back to local storage",
				zap.Error(err),
				zap.String("bucket", config.S3Bucket),
			)
			config.S3Enabled = false
		} else {
			config.S3StorageService = s3Service
			logger.Info("S3 storage service initialized successfully",
				zap.String("bucket", config.S3Bucket),
				zap.String("region", config.S3Region),
			)
		}
	}

	// Initialiser les services
	err = config.initServices()
	if err != nil {
		logger.Error("Failed to initialize services", zap.Error(err))
		return nil, err
	}

	// Initialiser les middlewares
	err = config.initMiddlewares()
	if err != nil {
		logger.Error("Failed to initialize middlewares", zap.Error(err))
		return nil, err
	}

	// Initialiser les métriques d'erreurs (T0020)
	config.ErrorMetrics = metrics.NewErrorMetrics()

	// Initialiser la configuration SMTP
	config.SMTPConfig = email.LoadSMTPConfigFromEnv()
	config.EmailSender = email.NewSMTPEmailSender(config.SMTPConfig, logger)

	// Initialiser le JobService
	jobService := services.NewJobService(logger)

	// Initialiser le JobWorker
	config.JobWorker = workers.NewJobWorker(
		config.Database.GormDB,
		jobService,
		logger,
		100,                // queueSize
		3,                  // workers
		3,                  // maxRetries
		config.EmailSender, // emailSender
	)

	// BE-SVC-003: Connect JobService to JobWorker
	jobService.SetJobEnqueuer(config.JobWorker)

	// Logger la configuration avec masquage des secrets (T0037)
	config.logConfigInitialized(logger)

	// Initialiser le ConfigWatcher si activé (T0040)
	// Le watcher peut être activé via une variable d'environnement CONFIG_WATCH=true
	if getEnv("CONFIG_WATCH", "false") == "true" {
		reloader := config.GetConfigReloader()
		watcher, err := NewConfigWatcher(reloader, logger)
		if err != nil {
			logger.Warn("Failed to create config watcher", zap.Error(err))
		} else {
			config.ConfigWatcher = watcher
			// Surveiller les fichiers .env
			envFiles := []string{".env", ".env." + env}
			if err := watcher.Watch(envFiles); err != nil {
				logger.Warn("Failed to start watching config files", zap.Error(err))
			} else {
				logger.Info("Config watcher started", zap.Strings("files", watcher.GetWatchedFiles()))
			}
		}
	}

	return config, nil
}

// GetConfigReloader retourne le ConfigReloader pour cette configuration (T0034)
func (c *Config) GetConfigReloader() *ConfigReloader {
	return NewConfigReloader(c, c.Logger)
}

// initServices initialise tous les services
func (c *Config) initServices() error {
	// Service de session
	c.SessionService = services.NewSessionService(c.Database, c.Logger)

	// Service d'audit
	c.AuditService = services.NewAuditService(c.Database, c.Logger)

	// Service TOTP
	c.TOTPService = services.NewTOTPService(c.Database, c.Logger)

	// Validateur d'upload
	uploadConfig := services.DefaultUploadConfig()
	// Lire ENABLE_CLAMAV depuis l'environnement (défaut: true pour sécurité en production)
	// En développement, peut être désactivé si ClamAV n'est pas disponible
	clamAVEnabled := getEnvBool("ENABLE_CLAMAV", true)
	uploadConfig.ClamAVEnabled = clamAVEnabled
	if !clamAVEnabled {
		c.Logger.Warn("ENABLE_CLAMAV=false - ClamAV virus scanning is disabled. This should only be used in development environments.")
	}
	// MOD-P1-002: Lire CLAMAV_REQUIRED depuis l'environnement (défaut: true pour sécurité)
	clamAVRequired := getEnvBool("CLAMAV_REQUIRED", true)
	uploadConfig.ClamAVRequired = clamAVRequired
	if !clamAVRequired {
		c.Logger.Warn("CLAMAV_REQUIRED=false - Uploads will be accepted even if ClamAV is unavailable (degraded mode). This should only be used in development or with alternative security measures.")
	}
	var err error
	c.UploadValidator, err = services.NewUploadValidator(uploadConfig, c.Logger)
	if err != nil {
		return err
	}

	// Service de cache
	c.CacheService = services.NewCacheService(c.RedisClient, c.Logger)

	// Service de playlist
	c.PlaylistService = services.NewPlaylistServiceWithDB(c.Database.GormDB, c.Logger)

	// Service de permissions
	c.PermissionService = services.NewPermissionService(c.Database.GormDB)

	// JWT Service
	c.JWTService, err = services.NewJWTService(c.JWTSecret, c.JWTIssuer, c.JWTAudience)
	if err != nil {
		return err
	}

	// User Service
	userRepo := repositories.NewGormUserRepository(c.Database.GormDB)
	c.UserService = services.NewUserServiceWithDB(userRepo, c.Database.GormDB)

	// BE-SVC-001: Set cache service for UserService
	if c.CacheService != nil {
		c.UserService.SetCacheService(c.CacheService)
	}

	// BE-SVC-001: Set cache service for PlaylistService
	if c.CacheService != nil && c.PlaylistService != nil {
		c.PlaylistService.SetCacheService(c.CacheService)
	}

	return nil
}

// initMiddlewares initialise tous les middlewares
func (c *Config) initMiddlewares() error {
	// Rate limiter global (avec Redis)
	// En développement, augmenter les limites pour éviter les erreurs lors des tests
	// Utiliser getEnvInt au lieu de getEnvAsInt (qui n'existe peut-être pas)
	ipLimit := getEnvInt("RATE_LIMIT_IP_PER_MINUTE", 200) // Augmenté de 100 à 200 en dev
	ipBurst := getEnvInt("RATE_LIMIT_IP_BURST", 20)       // Augmenté de 10 à 20 en dev
	userLimit := getEnvInt("RATE_LIMIT_USER_PER_MINUTE", 1000)
	userBurst := getEnvInt("RATE_LIMIT_USER_BURST", 100)

	rateLimiterConfig := &middleware.RateLimiterConfig{
		IPRequestsPerMinute:   ipLimit,
		IPBurst:               ipBurst,
		UserRequestsPerMinute: userLimit,
		UserBurst:             userBurst,
		RedisClient:           c.RedisClient,
		KeyPrefix:             "veza:rate_limit",
	}
	c.RateLimiter = middleware.NewRateLimiter(rateLimiterConfig)

	// Simple rate limiter (T0015) - sans dépendance Redis
	window := time.Duration(c.RateLimitWindow) * time.Second
	c.SimpleRateLimiter = middleware.NewSimpleRateLimiter(c.RateLimitLimit, window)

	// Rate limiter par endpoint
	endpointLimiterConfig := &middleware.EndpointLimiterConfig{
		RedisClient: c.RedisClient,
		KeyPrefix:   "veza:endpoint_limit",
	}
	endpointLimits := middleware.DefaultEndpointLimits()
	// Override defaults with config (PR-3)
	endpointLimits.LoginAttempts = c.AuthRateLimitLoginAttempts
	endpointLimits.LoginWindow = time.Duration(c.AuthRateLimitLoginWindow) * time.Minute

	c.EndpointLimiter = middleware.NewEndpointLimiter(endpointLimiterConfig, endpointLimits)

	// BE-SVC-002: Initialize per-user rate limiter
	userRateLimiterConfig := &middleware.UserRateLimiterConfig{
		RequestsPerMinute: getEnvAsInt("USER_RATE_LIMIT_PER_MINUTE", 1000), // Default: 1000 requests per minute per user
		Burst:             getEnvAsInt("USER_RATE_LIMIT_BURST", 100),       // Default: 100 burst
		Window:            time.Minute,
		RedisClient:       c.RedisClient,
		KeyPrefix:         "user_rate_limit",
		Logger:            c.Logger,
	}
	c.UserRateLimiter = middleware.NewUserRateLimiter(userRateLimiterConfig)

	// Middleware d'authentification
	c.AuthMiddleware = middleware.NewAuthMiddleware(
		c.SessionService,
		c.AuditService,
		c.PermissionService,
		c.JWTService,
		c.UserService,
		c.Logger,
	)

	return nil
}

// NOTE: Les handlers ne sont plus initialisés dans Config pour éviter les imports cycliques.
// Les handlers doivent être créés dans main.go ou dans les routes selon les besoins.
//
// SetupRoutes a été supprimé pour casser le cycle d'import config <-> api.
// Utiliser directement api.SetupRoutes() dans cmd/modern-server/main.go

// SetupMiddleware configure les middlewares globaux
// DÉPRÉCIÉ : Cette méthode est conservée pour compatibilité mais ne fait plus rien
// Les middlewares globaux sont maintenant configurés dans internal/api/router.go via APIRouter.Setup()
// TODO: Améliorer la configuration CORS dans api/router.go pour utiliser c.CORSOrigins depuis la config
func (c *Config) SetupMiddleware(router *gin.Engine) {
	// No-op : Les middlewares sont configurés dans api/router.go
	// Cette méthode existe uniquement pour compatibilité avec cmd/main.go (legacy)
	// qui sera désactivé dans le Chantier 1 - Étape 2
}

// initRedis initialise la connexion Redis
func initRedis(redisURL string, logger *zap.Logger) (*redis.Client, error) {
	opts, err := redis.ParseURL(redisURL)
	if err != nil {
		return nil, err
	}

	// Configurer un logger filtré pour Redis pour éviter les warnings "maint_notifications"
	redis.SetLogger(&filteredRedisLogger{logger: logger})

	client := redis.NewClient(opts)

	// Test de connexion
	ctx := context.Background()
	_, err = client.Ping(ctx).Result()
	if err != nil {
		return nil, err
	}

	return client, nil
}

// filteredRedisLogger est un wrapper pour filtrer les logs de Redis
type filteredRedisLogger struct {
	logger *zap.Logger
}

func (l *filteredRedisLogger) Printf(ctx context.Context, format string, v ...interface{}) {
	msg := fmt.Sprintf(format, v...)
	if strings.Contains(msg, "maint_notifications") {
		return // Ignorer ce warning spécifique en mode auto-discovery
	}
	l.logger.Debug("Redis internal", zap.String("message", msg))
}

// initDatabaseWithRetry initialise la connexion à la base de données avec des tentatives de retry
func initDatabaseWithRetry(databaseURL string, maxRetries int, retryInterval time.Duration, logger *zap.Logger) (*database.Database, error) {
	dbConfig := &database.Config{
		URL: databaseURL,
		// BE-DB-015: Optimized connection pool settings for production
		// MaxOpenConns: Recommended formula: (2 * CPU cores) + effective_spindle_count
		// Default: 25 for small-medium apps, 50-100 for high-traffic apps
		MaxOpenConns: getEnvAsInt("DB_MAX_OPEN_CONNS", 50),
		// MaxIdleConns: Should be ~25% of MaxOpenConns to maintain warm connections
		MaxIdleConns: getEnvAsInt("DB_MAX_IDLE_CONNS", 12),
		// MaxLifetime: 5-15 minutes recommended to avoid connection timeouts
		// PostgreSQL default idle_in_transaction_session_timeout is 0 (unlimited)
		MaxLifetime: getEnvAsDuration("DB_MAX_LIFETIME", 10*time.Minute),
		// MaxIdleTime: 5-10 minutes to close idle connections and free resources
		MaxIdleTime:   getEnvAsDuration("DB_MAX_IDLE_TIME", 5*time.Minute),
		MaxRetries:    maxRetries,
		RetryInterval: retryInterval,
	}

	// Utiliser la fonction de connexion avec retry du package database
	return database.NewDatabaseWithRetry(dbConfig, logger)
}

// EnvConfig représente la configuration de base chargée depuis les variables d'environnement
// Cette struct est utilisée par la fonction Load() pour charger la configuration de base
type EnvConfig struct {
	AppEnv      string
	AppPort     int
	DBHost      string
	DBPort      int
	DBUser      string
	DBPassword  string
	DBName      string
	JWTSecret   string
	RedisURL    string
	CORSOrigins []string // Liste des origines CORS autorisées
}

// Load charge et valide les variables d'environnement avec valeurs par défaut
func Load() (*EnvConfig, error) {
	// Déterminer l'environnement (T0032)
	env := getEnv("APP_ENV", "development")

	// Charger les fichiers .env selon l'environnement (T0032)
	// Charge dans l'ordre: .env.{env}, .env
	// Les variables d'environnement système ont priorité
	if err := LoadEnvFiles(env); err != nil {
		return nil, fmt.Errorf("failed to load environment files: %w", err)
	}

	// Charger les origines CORS depuis les variables d'environnement
	corsOrigins := getEnvStringSlice("CORS_ALLOWED_ORIGINS", []string{"*"})

	// Database, JWTSecret are required
	dbPassword, err := getEnvRequired("DB_PASSWORD")
	if err != nil {
		return nil, err
	}
	jwtSecret, err := getEnvRequired("JWT_SECRET")
	if err != nil {
		return nil, err
	}

	config := &EnvConfig{
		AppEnv:      getEnv("APP_ENV", "development"),
		AppPort:     getEnvInt("APP_PORT", 8080),
		DBHost:      getEnv("DB_HOST", "localhost"),
		DBPort:      getEnvInt("DB_PORT", 5432),
		DBUser:      getEnv("DB_USER", "veza"),
		DBPassword:  dbPassword,
		DBName:      getEnv("DB_NAME", "veza_db"),
		JWTSecret:   jwtSecret,
		RedisURL:    getEnv("REDIS_URL", "redis://localhost:6379"),
		CORSOrigins: corsOrigins,
	}

	return config, nil
}

// getEnv récupère une variable d'environnement avec une valeur par défaut
// SECURITY: Removed debug fmt.Printf to avoid leaking config info in production (P0-SECURITY)
func getEnv(key, defaultValue string) string {
	if value := os.Getenv(key); value != "" {
		return strings.TrimSpace(value)
	}
	return defaultValue
}

// getEnvAsInt retrieves an environment variable as an integer
// BE-DB-015: Helper for connection pool configuration
func getEnvAsInt(key string, defaultValue int) int {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	intValue, err := strconv.Atoi(strings.TrimSpace(value))
	if err != nil {
		return defaultValue
	}
	return intValue
}

// getEnvAsDuration retrieves an environment variable as a time.Duration
// BE-DB-015: Helper for connection pool configuration
func getEnvAsDuration(key string, defaultValue time.Duration) time.Duration {
	value := os.Getenv(key)
	if value == "" {
		return defaultValue
	}
	duration, err := time.ParseDuration(strings.TrimSpace(value))
	if err != nil {
		return defaultValue
	}
	return duration
}

// getEnvRequired récupère une variable d'environnement requise (retourne erreur si absente)
func getEnvRequired(key string) (string, error) {
	value := os.Getenv(key)
	if value == "" {
		return "", fmt.Errorf("required environment variable %s is not set", key)
	}
	return value, nil
}

// BE-SEC-014: getRabbitMQURL récupère l'URL RabbitMQ avec validation selon l'environnement
// En production, RABBITMQ_URL doit être explicitement défini (pas de valeur par défaut avec credentials)
func getRabbitMQURL(env string) string {
	rabbitMQURL := os.Getenv("RABBITMQ_URL")
	if rabbitMQURL != "" {
		return rabbitMQURL
	}

	// En production, ne pas utiliser de valeur par défaut avec credentials
	if env == EnvProduction {
		return "" // Will be validated in ValidateForEnvironment
	}

	// En développement, permettre la valeur par défaut pour le développement local uniquement
	return "amqp://guest:guest@localhost:5672/"
}

// getEnvInt récupère une variable d'environnement entière avec une valeur par défaut
func getEnvInt(key string, defaultValue int) int {
	if value := os.Getenv(key); value != "" {
		if intValue, err := strconv.Atoi(value); err == nil {
			return intValue
		}
	}
	return defaultValue
}

// getEnvBool récupère une variable d'environnement booléenne avec une valeur par défaut
func getEnvBool(key string, defaultValue bool) bool {
	if value := os.Getenv(key); value != "" {
		if boolValue, err := strconv.ParseBool(value); err == nil {
			return boolValue
		}
	}
	return defaultValue
}

// getEnvDuration récupère une variable d'environnement durée avec une valeur par défaut
func getEnvDuration(key string, defaultValue time.Duration) time.Duration {
	if value := os.Getenv(key); value != "" {
		if duration, err := time.ParseDuration(value); err == nil {
			return duration
		}
	}
	return defaultValue
}

// getEnvFloat64 récupère une variable d'environnement float64 avec une valeur par défaut
func getEnvFloat64(key string, defaultValue float64) float64 {
	if value := os.Getenv(key); value != "" {
		if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
			return floatValue
		}
	}
	return defaultValue
}

// getAuthRateLimitLoginAttempts retourne le nombre de tentatives de login autorisées
// Augmente les limites pour l'environnement de test/E2E
func getAuthRateLimitLoginAttempts(env string) int {
	// Vérifier si on est en mode test/E2E
	if env == "test" || env == "e2e" ||
		os.Getenv("GO_ENV") == "test" ||
		os.Getenv("GO_ENV") == "e2e" ||
		os.Getenv("E2E_TEST") == "true" {
		// Limite élevée pour les tests (100 tentatives)
		return getEnvInt("AUTH_RATE_LIMIT_LOGIN_ATTEMPTS", 100)
	}
	// Limite normale en production (5 tentatives)
	return getEnvInt("AUTH_RATE_LIMIT_LOGIN_ATTEMPTS", 5)
}

// getAuthRateLimitLoginWindow retourne la fenêtre de temps pour les tentatives de login
func getAuthRateLimitLoginWindow(env string) int {
	// En mode test, utiliser 1 minute (comme en production)
	// La fenêtre reste la même, seule la limite de tentatives change
	return getEnvInt("AUTH_RATE_LIMIT_LOGIN_WINDOW", 1)
}

// getEnvStringSlice récupère une variable d'environnement comme une slice de strings
// Format attendu: "value1,value2,value3" (séparées par des virgules)
func getEnvStringSlice(key string, defaultValue []string) []string {
	if value := os.Getenv(key); value != "" {
		// Séparer par virgule et nettoyer les espaces
		parts := strings.Split(value, ",")
		result := make([]string, 0, len(parts))
		for _, part := range parts {
			trimmed := strings.TrimSpace(part)
			if trimmed != "" {
				result = append(result, trimmed)
			}
		}
		if len(result) > 0 {
			return result
		}
	}
	return defaultValue
}

// parseLogAggregationLabels parse les labels d'agrégation de logs depuis une chaîne
// Format attendu: "key1=value1,key2=value2" (séparés par des virgules, key=value par paire)
func parseLogAggregationLabels(value string) map[string]string {
	labels := make(map[string]string)
	if value == "" {
		return labels
	}

	// Séparer par virgule
	pairs := strings.Split(value, ",")
	for _, pair := range pairs {
		pair = strings.TrimSpace(pair)
		if pair == "" {
			continue
		}

		// Séparer key=value
		parts := strings.SplitN(pair, "=", 2)
		if len(parts) == 2 {
			key := strings.TrimSpace(parts[0])
			val := strings.TrimSpace(parts[1])
			if key != "" && val != "" {
				labels[key] = val
			}
		}
	}

	return labels
}

// getCookieSecure détermine si les cookies doivent être Secure
// Auto-detect: secure en production, insecure en développement
// Peut être forcé via COOKIE_SECURE=true/false
func getCookieSecure(env string) bool {
	cookieSecureEnv := getEnv("COOKIE_SECURE", "")
	if cookieSecureEnv != "" {
		return getEnvBool("COOKIE_SECURE", false)
	}
	// Auto-detect: secure en production, insecure en développement
	return (env == EnvProduction)
}

// getCookieSameSite détermine la politique SameSite pour les cookies
// strict par défaut pour sécurité maximale, lax en développement local
func getCookieSameSite(env string) string {
	cookieSameSite := getEnv("COOKIE_SAME_SITE", "strict")
	if env == EnvDevelopment && cookieSameSite == "strict" {
		// En dev local, utiliser "lax" pour permettre localhost
		return "lax"
	}
	return cookieSameSite
}

// GetCookieSameSite retourne la valeur http.SameSite correspondante
func (c *Config) GetCookieSameSite() http.SameSite {
	switch c.CookieSameSite {
	case "lax":
		return http.SameSiteLaxMode
	case "none":
		return http.SameSiteNoneMode
	default:
		return http.SameSiteStrictMode
	}
}

// ShouldUseSecureCookies détermine si les cookies doivent être Secure
// Prend en compte la configuration explicite et l'environnement
func (c *Config) ShouldUseSecureCookies() bool {
	return c.CookieSecure
}

// getCORSOrigins charge les origines CORS avec defaults sécurisés selon l'environnement (P0-SECURITY)
// - development: defaults permissifs (localhost uniquement) si CORS_ALLOWED_ORIGINS non défini
// - test: liste vide ou configurée explicitement
// - production: CORS_ALLOWED_ORIGINS comportement:
//   - si défini: utiliser
//   - si absent/vide: liste vide (STRICT, reject all)
func getCORSOrigins(env string) []string {
	// Si CORS_ALLOWED_ORIGINS est défini, l'utiliser
	if value := os.Getenv("CORS_ALLOWED_ORIGINS"); value != "" {
		origins := getEnvStringSlice("CORS_ALLOWED_ORIGINS", nil)
		if len(origins) > 0 {
			return origins
		}
	}

	// Defaults selon l'environnement
	switch env {
	case EnvProduction:
		// Production: defaults to empty (Strict Mode)
		// MOD-P0-002: "si CORS_ALLOWED_ORIGINS vide, appliquer un comportement strict par défaut (reject toutes origines)"
		return []string{}
	case EnvTest:
		// Test: liste vide par défaut (peut être configurée explicitement)
		return []string{}
	case EnvDevelopment, EnvStaging:
		// Development/Staging: defaults permissifs pour localhost
		return []string{"http://localhost:3000", "http://127.0.0.1:3000", "http://localhost:5173", "http://127.0.0.1:5173"}
	default:
		// Fallback: development-like
		return []string{"http://localhost:3000", "http://127.0.0.1:3000"}
	}
}

// ValidateForEnvironment valide la configuration selon l'environnement (P0-SECURITY)
// En production: validation stricte (CORS requis, pas de wildcard, etc.)
// En development: validation permissive avec warnings
func (c *Config) ValidateForEnvironment() error {
	// D'abord, validation de base (port, secrets, URLs, etc.)
	if err := c.Validate(); err != nil {
		return err
	}

	// Validations spécifiques selon l'environnement
	switch c.Env {
	case EnvProduction:
		// PRODUCTION: Validation stricte

		// 1. MOD-P0-001: CORS_ALLOWED_ORIGINS MUST be configured in production (fail-fast)
		// Empty CORS origins means strict mode (reject all), which makes the service inaccessible from frontend
		if len(c.CORSOrigins) == 0 {
			return fmt.Errorf("CORS_ALLOWED_ORIGINS is required in production environment. Empty CORS origins will reject all CORS requests, making the service inaccessible from frontend. Please set CORS_ALLOWED_ORIGINS with explicit origins (e.g., CORS_ALLOWED_ORIGINS=https://app.veza.com,https://www.veza.com)")
		}

		// 2. CORS_ALLOWED_ORIGINS ne doit PAS contenir "*" (wildcard interdit en prod)
		for _, origin := range c.CORSOrigins {
			if origin == "*" {
				return fmt.Errorf("CORS wildcard '*' is not allowed in production environment. Please specify explicit origins in CORS_ALLOWED_ORIGINS")
			}
		}

		// 3. LogLevel ne doit pas être DEBUG en production
		if c.LogLevel == "DEBUG" {
			return fmt.Errorf("LOG_LEVEL=DEBUG is not allowed in production environment for security reasons")
		}

		// 4. BE-SEC-014: RabbitMQ URL must be explicitly set in production (no default with credentials)
		if c.RabbitMQEnable && c.RabbitMQURL == "" {
			return fmt.Errorf("RABBITMQ_URL is required in production when RabbitMQ is enabled. Do not use default credentials in production")
		}

	case EnvTest:
		// TEST: Validation adaptée aux tests
		// CORS peut être vide ou configuré explicitement
		// Pas de validation stricte sur les secrets (peuvent être des valeurs de test)

	case EnvDevelopment, EnvStaging:
		// DEVELOPMENT/STAGING: Validation permissive avec warnings
		// Si CORS contient "*", logger un warning mais ne pas bloquer
		for _, origin := range c.CORSOrigins {
			if origin == "*" {
				c.Logger.Warn("CORS wildcard '*' detected in development environment. This is acceptable for dev but should never be used in production")
				break
			}
		}
	}

	return nil
}

// Validate valide la configuration (T0031, T0036)
// Vérifie que toutes les valeurs de configuration sont valides avant le démarrage de l'application
// Utilise ConfigValidator pour une validation stricte selon les règles de schéma (T0036)
func (c *Config) Validate() error {
	validator := NewConfigValidator()

	// Valider le port (1-65535) avec ConfigValidator (T0036)
	if err := validator.ValidatePort(c.AppPort); err != nil {
		return fmt.Errorf("APP_PORT validation failed: %w", err)
	}

	// Valider JWT secret (minimum 32 caractères pour sécurité) avec ConfigValidator (T0036)
	if err := validator.ValidateSecretLength(c.JWTSecret, 32); err != nil {
		return fmt.Errorf("JWT_SECRET validation failed: %w", err)
	}

	// Valider DatabaseURL (requis) avec ConfigValidator (T0036)
	if c.DatabaseURL == "" {
		return errors.New("DATABASE_URL is required")
	}

	// Valider le format de DatabaseURL avec ConfigValidator (T0036)
	// Support postgres, postgresql, et sqlite
	if err := validator.ValidateURL(c.DatabaseURL, "postgres"); err != nil {
		if err2 := validator.ValidateURL(c.DatabaseURL, "postgresql"); err2 != nil {
			if err3 := validator.ValidateURL(c.DatabaseURL, "sqlite"); err3 != nil {
				return fmt.Errorf("DATABASE_URL validation failed: must start with postgres://, postgresql://, or sqlite://")
			}
		}
	}

	// Valider RedisURL (requis) avec ConfigValidator (T0036)
	if c.RedisURL == "" {
		return errors.New("REDIS_URL is required")
	}

	// Valider le format de RedisURL avec ConfigValidator (T0036)
	// Support redis et rediss (Redis avec SSL)
	if err := validator.ValidateURL(c.RedisURL, "redis"); err != nil {
		if err2 := validator.ValidateURL(c.RedisURL, "rediss"); err2 != nil {
			return fmt.Errorf("REDIS_URL validation failed: must start with redis:// or rediss://")
		}
	}

	// Valider LogLevel avec ValidateEnum (T0036)
	if c.LogLevel != "" {
		allowedLevels := []string{"DEBUG", "INFO", "WARN", "ERROR"}
		if err := validator.ValidateEnum(c.LogLevel, allowedLevels); err != nil {
			return fmt.Errorf("LOG_LEVEL validation failed: %w", err)
		}
	}

	// Valider RateLimitLimit et RateLimitWindow avec ValidatePositiveInt (T0036)
	if err := validator.ValidatePositiveInt(c.RateLimitLimit, "RATE_LIMIT_LIMIT"); err != nil {
		return fmt.Errorf("RATE_LIMIT_LIMIT validation failed: %w", err)
	}

	if err := validator.ValidatePositiveInt(c.RateLimitWindow, "RATE_LIMIT_WINDOW"); err != nil {
		return fmt.Errorf("RATE_LIMIT_WINDOW validation failed: %w", err)
	}

	return nil
}

// logConfigInitialized log la configuration initialisée avec masquage des secrets (T0037)
// MOD-P0-002: Always mask secrets in logs, even in DEBUG mode
func (c *Config) logConfigInitialized(logger *zap.Logger) {
	logger.Info("Configuration initialized successfully",
		zap.Int("app_port", c.AppPort),
		zap.String("jwt_secret", MaskConfigValue("JWT_SECRET", c.JWTSecret, c.SecretsProvider)),
		zap.String("jwt_issuer", c.JWTIssuer),
		zap.String("jwt_audience", c.JWTAudience),
		zap.String("chat_jwt_secret", MaskConfigValue("CHAT_JWT_SECRET", c.ChatJWTSecret, c.SecretsProvider)),
		zap.String("database_url", MaskConfigValue("DATABASE_URL", c.DatabaseURL, c.SecretsProvider)),
		zap.String("redis_url", MaskConfigValue("REDIS_URL", c.RedisURL, c.SecretsProvider)),
		zap.String("rabbitmq_url", MaskConfigValue("RABBITMQ_URL", c.RabbitMQURL, c.SecretsProvider)),
		zap.Strings("cors_origins", c.CORSOrigins),
		zap.Int("rate_limit_limit", c.RateLimitLimit),
		zap.Int("rate_limit_window", c.RateLimitWindow),
		zap.Int("auth_rate_limit_login_attempts", c.AuthRateLimitLoginAttempts),
		zap.Int("auth_rate_limit_login_window", c.AuthRateLimitLoginWindow),
		zap.Duration("handler_timeout", c.HandlerTimeout),
		zap.String("log_level", c.LogLevel),
		zap.String("sentry_dsn", MaskConfigValue("SENTRY_DSN", c.SentryDsn, c.SecretsProvider)),
	)
}

// Close ferme toutes les connexions (T0040)
func (c *Config) Close() error {
	var err error

	// Arrêter le ConfigWatcher si actif (T0040)
	if c.ConfigWatcher != nil {
		if closeErr := c.ConfigWatcher.Stop(); closeErr != nil {
			err = closeErr
		}
	}

	if c.RedisClient != nil {
		if closeErr := c.RedisClient.Close(); closeErr != nil {
			err = closeErr
		}
	}

	if c.Database != nil {
		if closeErr := c.Database.Close(); closeErr != nil {
			err = closeErr
		}
	}

	if c.RabbitMQEventBus != nil {
		if closeErr := c.RabbitMQEventBus.Close(); closeErr != nil {
			err = closeErr
		}
	}

	// FIX #4: Logger.Sync() est géré par le ShutdownManager dans main.go
	// Ne pas appeler Sync() ici pour éviter le double flush
	// Le ShutdownManager garantit le flush avec timeout et gestion d'erreur
	if c.Logger != nil {
		// Le logger sera sync'd par le ShutdownManager enregistré dans main.go
		// Pas besoin de Sync() ici car cela pourrait causer un double flush
		// et l'erreur serait ignorée de toute façon
	}

	return err
}
