import { z } from 'zod';

// Environment schema
const _EnvSchema = z.object({
	NODE_ENV: z.enum(['development', 'production', 'staging', 'test']),
	PORT: z.coerce.number().default(3001),
	BACKEND_URL: z.url(),
	FRONTEND_URL: z.string(),
	PROJECT_NAME: z.string().default('Bekind Backend'),
	PROJECT_VERSION: z.string().default('1.0.0'),
});

// Database schema
const DatabaseSchema = z.object({
	host: z.string().default('localhost'),
	port: z.coerce.number().default(5432),
	user: z.string(),
	password: z.string(),
	name: z.string(),
	connectionString: z.string().default(''),
	readHost: z.string().default(''),
	readPort: z.coerce.number().default(5432),
	slowQueryThresholdMs: z.coerce.number().default(500),
	pool: z.object({
		max: z.coerce.number().default(50),
		min: z.coerce.number().default(5),
		acquire: z.coerce.number().default(30000),
		idle: z.coerce.number().default(10000),
		evict: z.coerce.number().default(60000),
	}),
});

// Redis schema
const RedisSchema = z.object({
	enabled: z.coerce.boolean().default(false),
	host: z.string().default('localhost'),
	port: z.coerce.number().default(6379),
	password: z.string().default(''),
	db: z.coerce.number().default(0),
	connectTimeout: z.coerce.number().default(10000),
	commandTimeout: z.coerce.number().default(5000),
	maxRetries: z.coerce.number().default(3),
	ipFamily: z.coerce.number().default(4),
});

const RedisASchema = z.object({
	enabled: z.coerce.boolean().default(false),
	host: z.string().default('localhost'),
	port: z.coerce.number().default(6379),
	password: z.string().default(''),
	cluster: z.coerce.boolean().default(false),
	clusterNodes: z.string().default(''),
});

const RedisBSchema = z.object({
	host: z.string().default('localhost'),
	port: z.coerce.number().default(6380),
	password: z.string().default(''),
	db: z.coerce.number().default(0),
});

// Auth schema (highest priority - validated last)
const AuthSchema = z.object({
	jwtSecret: z.string().min(32, 'JWT_SECRET must be at least 32 characters'),
	jwtSecretPrevious: z.array(z.string()).optional(), // Previous secrets for rotation
	jwtRefreshSecret: z.string().min(32, 'JWT_REFRESH_SECRET must be at least 32 characters'),
	jwtRefreshSecretPrevious: z.array(z.string()).optional(), // Previous refresh secrets for rotation
	jwtExpiresIn: z.string().default('1h'),
	jwtRefreshExpiresIn: z.string().default('7d'),
	tokenEncryptionKey: z.string().min(32, 'TOKEN_ENCRYPTION_KEY must be at least 32 characters'),
	tokenEncryptionKeyPrevious: z.array(z.string()).optional(), // Previous encryption keys for rotation
	bcryptRounds: z.coerce.number().min(4).max(12).default(12),
	defaultPassword: z.string(),
	passwordMinLength: z.coerce.number().min(8).default(8),
	passwordMaxLength: z.coerce.number().max(128).default(128),
	includeTokensInResponse: z.coerce.boolean().default(false),
	skipSensitiveConfirm: z.coerce.boolean().default(false),
	enableRegister: z.coerce.boolean().default(true),
	passwordHistoryLimit: z.coerce.number().default(5),
});

// Cookie schema
const CookieSchema = z.object({
	domain: z.string().default('.yourdomain.com'),
	crossSite: z.coerce.boolean().default(false),
	forceSecure: z.coerce.boolean().default(true),
	devSameSiteLax: z.coerce.boolean().default(false),
	basePath: z.string().default(''),
});

// Upload schema
const UploadSchema = z.object({
	path: z.string().default('./storage/uploads'),
	maxFileSize: z.coerce.number().default(10485760),
	imagesPath: z.string().default('/storage/uploads/images'),
	videosPath: z.string().default('/storage/uploads/videos'),
	filesPath: z.string().default('/storage/uploads/files'),
	maxSize: z.coerce.number().default(10485760),
	storageImagesPath: z.string().default('./storage/images'),
	compressTimeoutMs: z.coerce.number().default(30000),
});

// Storage schema
const MinioSchema = z.object({
	endpoint: z.string().default('minio'),
	port: z.coerce.number().default(9000),
	useSSL: z.coerce.boolean().default(true),
	accessKey: z.string(),
	secretKey: z.string(),
	bucket: z.string().default('uploads'),
	region: z.string().default('us-east-1'),
	s3Bucket: z.string().default(''),
});

const StorageSchema = z.object({
	provider: z.enum(['local', 'minio']).default('minio'),
	publicBaseUrl: z.url(),
	localStorageRoot: z.string().default('./storage/uploads'),
	minio: MinioSchema,
	quotaPerUser: z.coerce.number().default(0),
});

// Email schema
const EmailSchema = z.object({
	host: z.string(),
	port: z.coerce.number(),
	user: z.string(),
	pass: z.string(),
	from: z.email(),
});

// Notifications schema
const QueueSchema = z.object({
	concurrency: z.coerce.number().default(10),
	workerConcurrency: z.coerce.number().default(20),
	attempts: z.coerce.number().default(3),
	backoffDelay: z.coerce.number().default(2000),
});

const RateLimitingSchema = z.object({
	maxLoginAttempts: z.coerce.number().default(5),
	lockoutDurationMinutes: z.coerce.number().default(15),
	rateLimitWindowMs: z.coerce.number().default(60000),
	rateLimitOnesignalMax: z.coerce.number().default(100),
	rateLimitZaloMax: z.coerce.number().default(50),
	rateLimitEmailMax: z.coerce.number().default(200),
});

const CacheTtlSchema = z.object({
	perm: z.coerce.number().default(300),
	session: z.coerce.number().default(900),
	reuseWindowSeconds: z.coerce.number().default(30),
});

const NotificationsSchema = z.object({
	queue: QueueSchema.optional(),
	rateLimiting: RateLimitingSchema,
	cacheTtl: CacheTtlSchema.optional(),
	maxRetries: z.coerce.number().default(3),
});

// CSRF schema
const CsrfSchema = z.object({
	originValidation: z.coerce.boolean().default(true),
	doubleSubmit: z.coerce.boolean().default(true),
	httpOnly: z.coerce.boolean().default(true),
	devSameSiteLax: z.coerce.boolean().default(false),
	allowedOrigins: z.array(z.string()).default([]),
});

// Zalo schema
const ZaloSchema = z.object({
	appId: z.string().default(''),
	secretKey: z.string().default(''),
	oaAccessToken: z.string().default(''),
	znsAccessToken: z.string().default(''),
	znsTemplateId: z.string().default(''),
	serviceJwtSecret: z.string().default(''),
	mtlsEnabled: z.coerce.boolean().default(false),
	workloadIdentityEnabled: z.coerce.boolean().default(false),
});

// OneSignal schema
const OneSignalSchema = z.object({
	appId: z.string().default(''),
	apiKey: z.string().default(''),
});

// VAPID schema
const VapidSchema = z.object({
	subject: z.string().default('mailto:admin@yourdomain.com'),
	publicKey: z.string().default(''),
	privateKey: z.string().default(''),
});

// Idempotency schema (TTL/Retry - low priority)
const IdempotencySchema = z.object({
	keyTtl: z.coerce.number().default(86400),
});

// Circuit Breaker schema (TTL/Retry - low priority)
const CircuitBreakerSchema = z.object({
	errorRate: z.coerce.number().default(0.3),
	minRequests: z.coerce.number().default(100),
	timeout: z.coerce.number().default(300000),
});

// Partition schema
const PartitionSchema = z.object({
	retainMonths: z.coerce.number().default(12),
});

// Virus scanning schema
const VirusScanSchema = z
	.object({
		enabled: z.coerce.boolean().default(false),
		clamSocket: z.string().default(''),
		clamHost: z.string().default('127.0.0.1'),
		clamPort: z.coerce.number().default(3310),
		clamScanTimeoutMs: z.coerce.number().default(15000),
		failOpen: z.coerce.boolean().default(false),
	})
	.partial();

// Logging schema (medium priority)
const LoggingSchema = z
	.object({
		level: z.enum(['error', 'warn', 'info', 'debug', 'off']).default('info'),
		url: z.string().default('./storage/logs'),
		mode: z.enum(['file', 'console', 'both']).default('file'),
		timeout: z.coerce.number().default(5000),
	})
	.partial();

// CORS schema
const CorsSchema = z.object({
	devDisable: z.coerce.boolean().default(false),
});

// Feature flags schema (medium priority)
const FeatureFlagsSchema = z.object({
	enableRedis: z.coerce.boolean().default(false),
	enableVirusScan: z.coerce.boolean().default(true),
	enableCSRF: z.coerce.boolean().default(true),
	showStackInErrors: z.coerce.boolean().default(false),
	fileListUrlConcurrency: z.coerce.number().default(5),
	maxSimpleFallbackSize: z.coerce.number().default(5000),
	maxFallbackMapSize: z.coerce.number().default(10000),
	enableRateLimit: z.coerce.boolean().default(true),
});

// Audit schema (deploy-time configuration)
const AuditSchema = z.object({
	mode: z.enum(['direct', 'worker']).default('direct'),
	enabled: z.coerce.boolean().default(true),
});

// Runtime config schema (safe to change at runtime)
const RuntimeConfigSchema = z.object({
	logLevel: z.enum(['error', 'warn', 'info', 'debug', 'off']).default('info'),
	batchSize: z.coerce.number().default(100),
	pollInterval: z.coerce.number().default(500),
});

// Webhook schema
const WebhookSchema = z.object({
	token: z.string().default(''),
});

// Complete config schema
export const ConfigSchema = z.object({
	server: z.object({
		port: z.coerce.number(),
		backendUrl: z.url(),
		frontendUrl: z.string(),
		projectName: z.string(),
		projectVersion: z.string(),
	}),
	database: DatabaseSchema,
	redis: RedisSchema,
	redisA: RedisASchema,
	redisB: RedisBSchema,
	auth: AuthSchema,
	cookie: CookieSchema,
	uploads: UploadSchema,
	storage: StorageSchema,
	email: EmailSchema,
	notifications: NotificationsSchema,
	csrf: CsrfSchema,
	zalo: ZaloSchema,
	oneSignal: OneSignalSchema,
	vapid: VapidSchema,
	idempotency: IdempotencySchema,
	circuitBreaker: CircuitBreakerSchema,
	partition: PartitionSchema,
	virusScan: VirusScanSchema,
	logging: LoggingSchema,
	cors: CorsSchema,
	featureFlags: FeatureFlagsSchema,
	audit: AuditSchema,
	runtime: RuntimeConfigSchema,
	webhook: WebhookSchema,
});

export type Config = z.infer<typeof ConfigSchema>;
