using System.Reflection;
using DodoSSH.Contracts;

namespace DodoSSH.Contracts.Tests;

/// <summary>
/// Problem codes are part of the public wire contract: the client switches on them.
/// </summary>
public sealed class ProblemCodesTests
{
    private static IReadOnlyList<FieldInfo> CodeFields =>
        [.. typeof(ProblemCodes)
            .GetFields(BindingFlags.Public | BindingFlags.Static)
            .Where(f => f.IsLiteral && f.FieldType == typeof(string))
            .Where(f => !string.Equals(f.Name, nameof(ProblemCodes.TypeBaseUri), StringComparison.Ordinal))];

    [Fact]
    public void AllCodes_AreUnique()
    {
        // A duplicated value would make two distinct failures indistinguishable to the
        // client, which is the whole point of having codes rather than parsing messages.
        var values = CodeFields.Select(f => (string)f.GetRawConstantValue()!).ToList();

        values.Distinct(StringComparer.Ordinal).Count().ShouldBe(values.Count);
    }

    [Fact]
    public void AllCodes_AreKebabCase()
    {
        // Codes are appended to TypeBaseUri to form the ProblemDetails type URI, so they
        // must be URL-safe and stylistically consistent.
        foreach (var field in CodeFields)
        {
            var value = (string)field.GetRawConstantValue()!;

            value.ShouldMatch("^[a-z][a-z0-9]*(-[a-z0-9]+)*$", $"{field.Name} is not kebab-case");
        }
    }

    [Fact]
    public void TypeBaseUri_IsAnAbsoluteHttpsUriEndingInSlash()
    {
        Uri.TryCreate(ProblemCodes.TypeBaseUri, UriKind.Absolute, out var uri).ShouldBeTrue();
        uri!.Scheme.ShouldBe(Uri.UriSchemeHttps);
        ProblemCodes.TypeBaseUri.ShouldEndWith("/");
    }
}
