using Microsoft.Extensions.Configuration;

namespace ZB.MOM.WW.Configuration;

/// <summary>
/// Fluent aggregator for validating raw <see cref="IConfiguration"/> BEFORE the host/DI container
/// exists (e.g. pre-Akka startup). Collects all failures and surfaces them together via
/// <see cref="ThrowIfInvalid"/>. For options that flow through DI, prefer
/// <see cref="ServiceCollectionExtensions.AddValidatedOptions{TOptions, TValidator}"/>.
/// </summary>
public sealed class ConfigPreflight
{
    private readonly IConfiguration _configuration;
    private readonly List<string> _failures = [];

    private ConfigPreflight(IConfiguration configuration) => _configuration = configuration;

    /// <summary>Starts a preflight over <paramref name="configuration"/>.</summary>
    public static ConfigPreflight For(IConfiguration configuration)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        return new ConfigPreflight(configuration);
    }

    /// <summary>The accumulated failure messages (empty when valid).</summary>
    public IReadOnlyList<string> Failures => _failures;

    /// <summary>True when no failures have been accumulated.</summary>
    public bool IsValid => _failures.Count == 0;

    /// <summary>Requires the value at <paramref name="key"/> to satisfy <paramref name="predicate"/>.</summary>
    public ConfigPreflight Require(string key, Func<string?, bool> predicate, string reason)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(key);
        ArgumentNullException.ThrowIfNull(predicate);
        if (!predicate(_configuration[key])) _failures.Add($"{key} {reason}");
        return this;
    }

    /// <summary>Requires a non-empty value at <paramref name="key"/>.</summary>
    public ConfigPreflight RequireValue(string key)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(key);
        return AddIf(Checks.Required(_configuration[key], key));
    }

    /// <summary>Requires a valid integer TCP port (1-65535) at <paramref name="key"/>.</summary>
    public ConfigPreflight RequirePort(string key)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(key);
        return AddIf(Checks.PortValue(_configuration[key], key));
    }

    /// <summary>Runs <paramref name="block"/> only when <paramref name="condition"/> holds (role-conditional rules).</summary>
    public ConfigPreflight When(bool condition, Action<ConfigPreflight> block)
    {
        ArgumentNullException.ThrowIfNull(block);
        if (condition) block(this);
        return this;
    }

    /// <summary>Throws <see cref="InvalidOperationException"/> listing all failures when invalid; otherwise returns.</summary>
    public void ThrowIfInvalid()
    {
        if (_failures.Count > 0)
            throw new InvalidOperationException(
                $"Configuration validation failed:\n{string.Join("\n", _failures.Select(e => $"  - {e}"))}");
    }

    private ConfigPreflight AddIf(string? message)
    {
        if (message is not null) _failures.Add(message);
        return this;
    }
}
