using Microsoft.Extensions.Configuration;
using ZB.MOM.WW.Configuration;

namespace ZB.MOM.WW.Configuration.Tests;

public sealed class ConfigPreflightTests
{
    private static IConfiguration Config(Dictionary<string, string?> values) =>
        new ConfigurationBuilder().AddInMemoryCollection(values).Build();

    [Fact]
    public void Aggregates_all_failures()
    {
        var cfg = Config(new() { ["Node:Role"] = "Bogus", ["Node:RemotingPort"] = "0" });
        var pf = ConfigPreflight.For(cfg)
            .Require("Node:Role", v => v is "Central" or "Site", "must be 'Central' or 'Site'")
            .RequirePort("Node:RemotingPort");
        Assert.False(pf.IsValid);
        Assert.Equal(2, pf.Failures.Count);
    }

    [Fact]
    public void When_runs_block_only_if_condition_true()
    {
        var cfg = Config(new() { ["Node:Role"] = "Site" });
        var pf = ConfigPreflight.For(cfg)
            .When(cfg["Node:Role"] == "Site",
                p => p.RequireValue("Node:SiteId"));
        Assert.False(pf.IsValid);              // SiteId missing
        Assert.Contains(pf.Failures, f => f.Contains("Node:SiteId"));
    }

    [Fact]
    public void When_false_does_not_run_block()
    {
        var cfg = Config(new() { ["Node:Role"] = "Central" });
        var pf = ConfigPreflight.For(cfg)
            .When(cfg["Node:Role"] == "Site", p => p.RequireValue("Node:SiteId"));
        Assert.True(pf.IsValid);   // block skipped, no failure recorded
    }

    [Fact]
    public void ThrowIfInvalid_throws_aggregated_message()
    {
        var cfg = Config(new() { ["Node:Name"] = "" });
        var ex = Assert.Throws<InvalidOperationException>(() =>
            ConfigPreflight.For(cfg).RequireValue("Node:Name").ThrowIfInvalid());
        Assert.StartsWith("Configuration validation failed:", ex.Message);
        Assert.Contains("  - Node:Name", ex.Message);
    }

    [Fact]
    public void ThrowIfInvalid_is_noop_when_valid()
    {
        var cfg = Config(new() { ["Node:Name"] = "ok" });
        ConfigPreflight.For(cfg).RequireValue("Node:Name").ThrowIfInvalid();   // does not throw
    }
}
