/**
 * Validates required environment variables at application startup.
 * Throws if any critical variable is missing to prevent silent misconfigurations.
 */

import LoggingService from '#services/file-system/logService';

interface EnvRule {
	key: string;
	required: boolean;
	redact?: boolean;
}

const REQUIRED_ENVS: EnvRule[] = [
	{ key: 'NODE_ENV', required: true },
	{ key: 'PORT', required: false },
	{ key: 'JWT_SECRET', required: true, redact: true },
	{ key: 'JWT_REFRESH_SECRET', required: true, redact: true },
	{ key: 'JWT_EXPIRES_IN', required: false },
	{ key: 'JWT_REFRESH_EXPIRES_IN', required: false },
	{ key: 'TOKEN_ENCRYPTION_KEY', required: true, redact: true },
	{ key: 'DB_HOST', required: true },
	{ key: 'DB_PORT', required: false },
	{ key: 'DB_NAME', required: true },
	{ key: 'DB_USER', required: true },
	{ key: 'DB_PASSWORD', required: true, redact: true },
	{ key: 'BCRYPT_ROUNDS', required: false },
	// Warn if BCRYPT_ROUNDS is too low
	{ key: 'REDIS_HOST', required: false }, // Legacy - optional if REDIS_A_HOST is set
	{ key: 'REDIS_PORT', required: false },
	{ key: 'REDIS_PASSWORD', required: false, redact: true },
	{ key: 'REDIS_DB', required: false },
	// SEC-04: Add Redis A/B connection validation for BullMQ and cache
	{ key: 'REDIS_A_HOST', required: false }, // At least one of REDIS_A_HOST or REDIS_HOST must be set
	{ key: 'REDIS_A_PORT', required: false },
	{ key: 'REDIS_A_PASSWORD', required: false, redact: true },
	{ key: 'REDIS_A_DB', required: false },
	{ key: 'REDIS_B_HOST', required: false },
	{ key: 'REDIS_B_PORT', required: false },
	{ key: 'REDIS_B_PASSWORD', required: false, redact: true },
	{ key: 'REDIS_B_DB', required: false },
	{ key: 'EMAIL_USER', required: false },
	{ key: 'EMAIL_PASS', required: false, redact: true },
	{ key: 'ZALO_OA_ACCESS_TOKEN', required: false, redact: true },
	{ key: 'FRONTEND_URL', required: false },
];

const WEAK_SECRET_PATTERN = /^(secret|password|test|example|changeme|dev|default|123)/i;
const MIN_SECRET_LENGTH = 32;

export function validateEnv(): void {
	const missing: string[] = [];
	const warnings: string[] = [];
	// eslint-disable-next-line no-restricted-syntax
	const isProduction = process.env.NODE_ENV === 'production';

	for (const rule of REQUIRED_ENVS) {
		// eslint-disable-next-line no-restricted-syntax
		const value = process.env[rule.key];

		if (rule.required && !value) {
			missing.push(rule.key);
			continue;
		}

		// Warn about weak secrets in production
		if (isProduction && rule.redact && value) {
			if (value.length < MIN_SECRET_LENGTH) {
				warnings.push(`${rule.key} is too short (min ${MIN_SECRET_LENGTH} chars recommended)`);
			}
			if (WEAK_SECRET_PATTERN.test(value)) {
				warnings.push(`${rule.key} looks like a weak/default secret`);
			}
		}

		// Warn about low BCRYPT_ROUNDS in production
		if (isProduction && rule.key === 'BCRYPT_ROUNDS' && value) {
			const rounds = parseInt(value, 10);
			if (rounds < 12) {
				warnings.push(`BCRYPT_ROUNDS is ${rounds}, minimum 12 recommended for production`);
			}
		}
	}

	// SEC-04: Ensure at least one Redis connection is configured (legacy or A/B)
	// eslint-disable-next-line no-restricted-syntax
	const hasLegacyRedis = process.env.REDIS_HOST;
	// eslint-disable-next-line no-restricted-syntax
	const hasRedisA = process.env.REDIS_A_HOST;
	// eslint-disable-next-line no-restricted-syntax
	const hasRedisB = process.env.REDIS_B_HOST;

	if (!hasLegacyRedis && !hasRedisA && !hasRedisB) {
		missing.push('REDIS_HOST or REDIS_A_HOST');
	}

	if (missing.length > 0) {
		throw new Error(`[ENV] Missing required environment variables: ${missing.join(', ')}`);
	}

	if (warnings.length > 0) {
		const loggingService = new LoggingService();
		for (const w of warnings) {
			loggingService.logAsync('warn', 'Env', `[ENV][WARN] ${w}`, undefined, undefined).catch((err) => {
				// Fallback to console if async logging fails
				console.error(`[ENV][WARN] ${w}`);
				console.error('Failed to log warning:', err);
			});
		}
	}
}
