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

namespace ZB.MOM.WW.Configuration.Tests;

/// <summary>
/// Pins the exact failure-message wording produced by the shared <c>Checks</c> seam through its
/// public front-ends (<see cref="ConfigPreflight"/> for raw port values, <see cref="ValidationBuilder"/>
/// for host:port endpoints). Covers Configuration-002 (consistent quoting) and Configuration-003
/// (strict, culture-invariant port parsing).
/// </summary>
public sealed class ChecksWordingTests
{
    private static IConfiguration Config(string key, string? value) =>
        new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?> { [key] = value })
            .Build();

    private static string PortFailure(string? rawValue)
    {
        var pf = ConfigPreflight.For(Config("X:Port", rawValue)).RequirePort("X:Port");
        return Assert.Single(pf.Failures);
    }

    // Configuration-002: range failure and parse failure must quote the offending value the same way.

    [Fact]
    public void PortValue_range_failure_quotes_the_value()
    {
        Assert.Equal("X:Port must be between 1 and 65535 (was '0')", PortFailure("0"));
    }

    [Fact]
    public void PortValue_high_range_failure_quotes_the_value()
    {
        Assert.Equal("X:Port must be between 1 and 65535 (was '70000')", PortFailure("70000"));
    }

    [Fact]
    public void PortValue_parse_failure_quotes_the_value()
    {
        Assert.Equal("X:Port must be between 1 and 65535 (was 'notaport')", PortFailure("notaport"));
    }

    [Fact]
    public void PortValue_null_failure_renders_null()
    {
        Assert.Equal("X:Port must be between 1 and 65535 (was 'null')", PortFailure(null));
    }

    // Configuration-003: strict, culture-invariant parsing rejects sign and surrounding whitespace.

    [Theory]
    [InlineData("+5000")]
    [InlineData(" 5000")]
    [InlineData("5000 ")]
    [InlineData(" 5000 ")]
    [InlineData("-1")]
    public void PortValue_rejects_loose_inputs(string raw)
    {
        var pf = ConfigPreflight.For(Config("X:Port", raw)).RequirePort("X:Port");
        Assert.False(pf.IsValid);
        Assert.Equal($"X:Port must be between 1 and 65535 (was '{raw}')", Assert.Single(pf.Failures));
    }

    [Fact]
    public void PortValue_accepts_plain_in_range_port()
    {
        var pf = ConfigPreflight.For(Config("X:Port", "5000")).RequirePort("X:Port");
        Assert.True(pf.IsValid);
    }

    [Theory]
    [InlineData("host:+5000")]
    [InlineData("host: 5000")]
    [InlineData("host:5000 ")]
    public void HostPort_rejects_loose_port_inputs(string value)
    {
        var b = new ValidationBuilder();
        b.HostPort(value, "X:Endpoint");
        Assert.False(b.IsValid);
        Assert.Equal($"X:Endpoint must be 'host:port' with port 1-65535 (was '{value}')", Assert.Single(b.Failures));
    }

    [Fact]
    public void HostPort_accepts_plain_endpoint()
    {
        var b = new ValidationBuilder();
        b.HostPort("host:5000", "X:Endpoint");
        Assert.True(b.IsValid);
    }
}
