using System.Reflection;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;

namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Messages;

/// <summary>
/// Tests for <see cref="ManagementCommandRegistry"/>, including the Commons-004
/// regression: <c>GetCommandName</c> and <c>Resolve</c> must be symmetric — every
/// type for which <c>GetCommandName</c> yields a name must round-trip back to the
/// same type via <c>Resolve</c>.
/// </summary>
public class ManagementCommandRegistryTests
{
    private static IEnumerable<Type> RegisteredCommandTypes() =>
        typeof(ManagementEnvelope).Assembly.GetTypes()
            .Where(t => t.Namespace == typeof(ManagementEnvelope).Namespace
                        && t.Name.EndsWith("Command", StringComparison.Ordinal)
                        && !t.IsAbstract);

    [Fact]
    public void GetCommandName_Resolve_RoundTrips_ForEveryRegisteredCommand()
    {
        foreach (var type in RegisteredCommandTypes())
        {
            var name = ManagementCommandRegistry.GetCommandName(type);
            var resolved = ManagementCommandRegistry.Resolve(name);
            Assert.Equal(type, resolved);
        }
    }

    [Fact]
    public void Resolve_KnownCommand_ReturnsType()
    {
        var type = ManagementCommandRegistry.Resolve("CreateSite");
        Assert.Equal(typeof(CreateSiteCommand), type);
    }

    [Fact]
    public void Resolve_UnknownCommand_ReturnsNull()
    {
        Assert.Null(ManagementCommandRegistry.Resolve("NoSuchCommand"));
    }

    [Fact]
    public void Resolve_IsCaseInsensitive()
    {
        Assert.Equal(typeof(CreateSiteCommand), ManagementCommandRegistry.Resolve("createsite"));
    }

    /// <summary>
    /// Commons-004: <c>GetCommandName</c> previously stripped a <c>Command</c> suffix
    /// from <em>any</em> type, producing names the registry cannot resolve. It must
    /// only return a name for a command type the registry actually contains.
    /// </summary>
    [Fact]
    public void GetCommandName_UnregisteredCommandType_Throws()
    {
        // A *Command type that is not in the Messages.Management namespace.
        Assert.Throws<ArgumentException>(
            () => ManagementCommandRegistry.GetCommandName(typeof(UnregisteredFakeCommand)));
    }

    [Fact]
    public void GetCommandName_NonCommandType_Throws()
    {
        Assert.Throws<ArgumentException>(
            () => ManagementCommandRegistry.GetCommandName(typeof(string)));
    }

    [Fact]
    public void GetCommandName_RegisteredCommand_ReturnsStrippedName()
    {
        Assert.Equal("CreateSite", ManagementCommandRegistry.GetCommandName(typeof(CreateSiteCommand)));
    }

    [Fact]
    public void Resolve_NativeAlarmSourceCommands_AllRegistered()
    {
        Assert.Equal(typeof(AddTemplateNativeAlarmSourceCommand),
            ManagementCommandRegistry.Resolve("AddTemplateNativeAlarmSource"));
        Assert.Equal(typeof(UpdateTemplateNativeAlarmSourceCommand),
            ManagementCommandRegistry.Resolve("UpdateTemplateNativeAlarmSource"));
        Assert.Equal(typeof(DeleteTemplateNativeAlarmSourceCommand),
            ManagementCommandRegistry.Resolve("DeleteTemplateNativeAlarmSource"));
        Assert.Equal(typeof(ListTemplateNativeAlarmSourcesCommand),
            ManagementCommandRegistry.Resolve("ListTemplateNativeAlarmSources"));
        Assert.Equal(typeof(SetInstanceNativeAlarmSourceOverrideCommand),
            ManagementCommandRegistry.Resolve("SetInstanceNativeAlarmSourceOverride"));
        Assert.Equal(typeof(DeleteInstanceNativeAlarmSourceOverrideCommand),
            ManagementCommandRegistry.Resolve("DeleteInstanceNativeAlarmSourceOverride"));
        Assert.Equal(typeof(ListInstanceNativeAlarmSourceOverridesCommand),
            ManagementCommandRegistry.Resolve("ListInstanceNativeAlarmSourceOverrides"));
    }

    /// <summary>
    /// M9-T32c: the schema-library CRUD commands must auto-register by reflection
    /// (no manual registry entry) so the HTTP management boundary can route them.
    /// </summary>
    [Fact]
    public void Resolve_SchemaLibraryCommands_AllRegistered()
    {
        Assert.Equal(typeof(ListSharedSchemasCommand),
            ManagementCommandRegistry.Resolve("ListSharedSchemas"));
        Assert.Equal(typeof(GetSharedSchemaCommand),
            ManagementCommandRegistry.Resolve("GetSharedSchema"));
        Assert.Equal(typeof(CreateSharedSchemaCommand),
            ManagementCommandRegistry.Resolve("CreateSharedSchema"));
        Assert.Equal(typeof(UpdateSharedSchemaCommand),
            ManagementCommandRegistry.Resolve("UpdateSharedSchema"));
        Assert.Equal(typeof(DeleteSharedSchemaCommand),
            ManagementCommandRegistry.Resolve("DeleteSharedSchema"));
    }

    /// <summary>A *Command record outside the Management namespace, for the negative test.</summary>
    private record UnregisteredFakeCommand(int Id);
}
