using System;
using System.Collections.Generic;
using System.Linq;
using Serilog;

namespace ZB.MOM.WW.LmxProxy.Host.Configuration
{
    /// <summary>
    ///     Validates LmxProxy configuration settings on startup.
    /// </summary>
    public static class ConfigurationValidator
    {
        private static readonly ILogger Logger = Log.ForContext(typeof(ConfigurationValidator));

        /// <summary>
        ///     Validates the provided configuration and returns a list of validation errors.
        /// </summary>
        /// <param name="configuration">The configuration to validate.</param>
        /// <returns>A list of validation error messages. Empty if configuration is valid.</returns>
        public static List<string> Validate(LmxProxyConfiguration configuration)
        {
            var errors = new List<string>();

            if (configuration == null)
            {
                errors.Add("Configuration is null");
                return errors;
            }

            // Validate gRPC port
            if (configuration.GrpcPort <= 0 || configuration.GrpcPort > 65535)
            {
                errors.Add($"Invalid gRPC port: {configuration.GrpcPort}. Must be between 1 and 65535.");
            }

            // Validate API key configuration file
            if (string.IsNullOrWhiteSpace(configuration.ApiKeyConfigFile))
            {
                errors.Add("API key configuration file path is not specified.");
            }

            // Validate Connection settings
            if (configuration.Connection != null)
            {
                ValidateConnectionConfiguration(configuration.Connection, errors);
            }
            else
            {
                errors.Add("Connection configuration is missing.");
            }

            // Validate Subscription settings
            if (configuration.Subscription != null)
            {
                ValidateSubscriptionConfiguration(configuration.Subscription, errors);
            }

            // Validate Service Recovery settings
            if (configuration.ServiceRecovery != null)
            {
                ValidateServiceRecoveryConfiguration(configuration.ServiceRecovery, errors);
            }

            // Validate TLS settings
            if (configuration.Tls != null)
            {
                if (!configuration.Tls.Validate())
                {
                    errors.Add("TLS configuration validation failed. Check the logs for details.");
                }
            }

            return errors;
        }

        private static void ValidateConnectionConfiguration(ConnectionConfiguration config, List<string> errors)
        {
            if (config.MonitorIntervalSeconds <= 0)
            {
                errors.Add(
                    $"Invalid monitor interval: {config.MonitorIntervalSeconds} seconds. Must be greater than 0.");
            }

            if (config.ConnectionTimeoutSeconds <= 0)
            {
                errors.Add(
                    $"Invalid connection timeout: {config.ConnectionTimeoutSeconds} seconds. Must be greater than 0.");
            }

            if (config.ReadTimeoutSeconds <= 0)
            {
                errors.Add($"Invalid read timeout: {config.ReadTimeoutSeconds} seconds. Must be greater than 0.");
            }

            if (config.WriteTimeoutSeconds <= 0)
            {
                errors.Add($"Invalid write timeout: {config.WriteTimeoutSeconds} seconds. Must be greater than 0.");
            }

            if (config.MaxConcurrentOperations.HasValue && config.MaxConcurrentOperations.Value <= 0)
            {
                errors.Add(
                    $"Invalid max concurrent operations: {config.MaxConcurrentOperations}. Must be greater than 0.");
            }

            // Validate node and galaxy names if provided
            if (!string.IsNullOrWhiteSpace(config.NodeName) && config.NodeName?.Length > 255)
            {
                errors.Add($"Node name is too long: {config.NodeName.Length} characters. Maximum is 255.");
            }

            if (!string.IsNullOrWhiteSpace(config.GalaxyName) && config.GalaxyName?.Length > 255)
            {
                errors.Add($"Galaxy name is too long: {config.GalaxyName.Length} characters. Maximum is 255.");
            }
        }

        private static void ValidateSubscriptionConfiguration(SubscriptionConfiguration config, List<string> errors)
        {
            if (config.ChannelCapacity <= 0)
            {
                errors.Add($"Invalid channel capacity: {config.ChannelCapacity}. Must be greater than 0.");
            }

            if (config.ChannelCapacity > 100000)
            {
                errors.Add($"Channel capacity too large: {config.ChannelCapacity}. Maximum recommended is 100000.");
            }

            string[] validChannelModes = { "DropOldest", "DropNewest", "Wait" };
            if (!validChannelModes.Contains(config.ChannelFullMode))
            {
                errors.Add(
                    $"Invalid channel full mode: {config.ChannelFullMode}. Valid values are: {string.Join(", ", validChannelModes)}");
            }
        }

        private static void ValidateServiceRecoveryConfiguration(ServiceRecoveryConfiguration config,
            List<string> errors)
        {
            if (config.FirstFailureDelayMinutes < 0)
            {
                errors.Add(
                    $"Invalid first failure delay: {config.FirstFailureDelayMinutes} minutes. Must be 0 or greater.");
            }

            if (config.SecondFailureDelayMinutes < 0)
            {
                errors.Add(
                    $"Invalid second failure delay: {config.SecondFailureDelayMinutes} minutes. Must be 0 or greater.");
            }

            if (config.SubsequentFailureDelayMinutes < 0)
            {
                errors.Add(
                    $"Invalid subsequent failure delay: {config.SubsequentFailureDelayMinutes} minutes. Must be 0 or greater.");
            }

            if (config.ResetPeriodDays <= 0)
            {
                errors.Add($"Invalid reset period: {config.ResetPeriodDays} days. Must be greater than 0.");
            }
        }

        /// <summary>
        ///     Logs validation results and returns whether the configuration is valid.
        /// </summary>
        /// <param name="configuration">The configuration to validate.</param>
        /// <returns>True if configuration is valid; otherwise, false.</returns>
        public static bool ValidateAndLog(LmxProxyConfiguration configuration)
        {
            List<string> errors = Validate(configuration);

            if (errors.Any())
            {
                Logger.Error("Configuration validation failed with {ErrorCount} errors:", errors.Count);
                foreach (string? error in errors)
                {
                    Logger.Error("  - {ValidationError}", error);
                }

                return false;
            }

            Logger.Information("Configuration validation successful");
            return true;
        }

        /// <summary>
        ///     Throws an exception if the configuration is invalid.
        /// </summary>
        /// <param name="configuration">The configuration to validate.</param>
        /// <exception cref="InvalidOperationException">Thrown when configuration is invalid.</exception>
        public static void ValidateOrThrow(LmxProxyConfiguration configuration)
        {
            List<string> errors = Validate(configuration);

            if (errors.Any())
            {
                string message = $"Configuration validation failed with {errors.Count} error(s):\n" +
                                 string.Join("\n", errors.Select(e => $"  - {e}"));
                throw new InvalidOperationException(message);
            }
        }
    }
}
