using System;
using System.Collections.Generic;
using System.IO;
using Serilog;
namespace ZB.MOM.WW.LmxProxy.Host.Configuration
{
///
/// Validates the LmxProxy configuration at startup.
/// Throws InvalidOperationException on any validation error.
///
public static class ConfigurationValidator
{
private static readonly ILogger Log = Serilog.Log.ForContext(typeof(ConfigurationValidator));
///
/// Validates all configuration settings and logs the effective values.
/// Throws on first validation error.
///
public static void ValidateAndLog(LmxProxyConfiguration config)
{
var errors = new List();
// GrpcPort
if (config.GrpcPort < 1 || config.GrpcPort > 65535)
errors.Add($"GrpcPort must be 1-65535, got {config.GrpcPort}");
// Connection
var conn = config.Connection;
if (conn.MonitorIntervalSeconds <= 0)
errors.Add($"Connection.MonitorIntervalSeconds must be > 0, got {conn.MonitorIntervalSeconds}");
if (conn.ConnectionTimeoutSeconds <= 0)
errors.Add($"Connection.ConnectionTimeoutSeconds must be > 0, got {conn.ConnectionTimeoutSeconds}");
if (conn.ReadTimeoutSeconds <= 0)
errors.Add($"Connection.ReadTimeoutSeconds must be > 0, got {conn.ReadTimeoutSeconds}");
if (conn.WriteTimeoutSeconds <= 0)
errors.Add($"Connection.WriteTimeoutSeconds must be > 0, got {conn.WriteTimeoutSeconds}");
if (conn.MaxConcurrentOperations <= 0)
errors.Add($"Connection.MaxConcurrentOperations must be > 0, got {conn.MaxConcurrentOperations}");
if (conn.NodeName != null && conn.NodeName.Length > 255)
errors.Add("Connection.NodeName must be <= 255 characters");
if (conn.GalaxyName != null && conn.GalaxyName.Length > 255)
errors.Add("Connection.GalaxyName must be <= 255 characters");
// Subscription
var sub = config.Subscription;
if (sub.ChannelCapacity < 0 || sub.ChannelCapacity > 100000)
errors.Add($"Subscription.ChannelCapacity must be 0-100000, got {sub.ChannelCapacity}");
var validModes = new HashSet(StringComparer.OrdinalIgnoreCase)
{ "DropOldest", "DropNewest", "Wait" };
if (!validModes.Contains(sub.ChannelFullMode))
errors.Add($"Subscription.ChannelFullMode must be DropOldest, DropNewest, or Wait, got '{sub.ChannelFullMode}'");
// ServiceRecovery
var sr = config.ServiceRecovery;
if (sr.FirstFailureDelayMinutes < 0)
errors.Add($"ServiceRecovery.FirstFailureDelayMinutes must be >= 0, got {sr.FirstFailureDelayMinutes}");
if (sr.SecondFailureDelayMinutes < 0)
errors.Add($"ServiceRecovery.SecondFailureDelayMinutes must be >= 0, got {sr.SecondFailureDelayMinutes}");
if (sr.SubsequentFailureDelayMinutes < 0)
errors.Add($"ServiceRecovery.SubsequentFailureDelayMinutes must be >= 0, got {sr.SubsequentFailureDelayMinutes}");
if (sr.ResetPeriodDays <= 0)
errors.Add($"ServiceRecovery.ResetPeriodDays must be > 0, got {sr.ResetPeriodDays}");
// TLS
if (config.Tls.Enabled)
{
if (!File.Exists(config.Tls.ServerCertificatePath))
Log.Warning("TLS enabled but server certificate not found at {Path} (will auto-generate)",
config.Tls.ServerCertificatePath);
if (!File.Exists(config.Tls.ServerKeyPath))
Log.Warning("TLS enabled but server key not found at {Path} (will auto-generate)",
config.Tls.ServerKeyPath);
}
// WebServer
if (config.WebServer.Enabled)
{
if (config.WebServer.Port < 1 || config.WebServer.Port > 65535)
errors.Add($"WebServer.Port must be 1-65535, got {config.WebServer.Port}");
}
if (errors.Count > 0)
{
foreach (var error in errors)
Log.Error("Configuration error: {Error}", error);
throw new InvalidOperationException(
$"Configuration validation failed with {errors.Count} error(s): {string.Join("; ", errors)}");
}
// Log effective configuration
Log.Information("Configuration validated successfully");
Log.Information(" GrpcPort: {Port}", config.GrpcPort);
Log.Information(" ApiKeyConfigFile: {File}", config.ApiKeyConfigFile);
Log.Information(" Connection.AutoReconnect: {AutoReconnect}", conn.AutoReconnect);
Log.Information(" Connection.MonitorIntervalSeconds: {Interval}", conn.MonitorIntervalSeconds);
Log.Information(" Connection.MaxConcurrentOperations: {Max}", conn.MaxConcurrentOperations);
Log.Information(" Subscription.ChannelCapacity: {Capacity}", sub.ChannelCapacity);
Log.Information(" Subscription.ChannelFullMode: {Mode}", sub.ChannelFullMode);
Log.Information(" Tls.Enabled: {Enabled}", config.Tls.Enabled);
Log.Information(" WebServer.Enabled: {Enabled}, Port: {Port}", config.WebServer.Enabled, config.WebServer.Port);
}
}
}