using System.CommandLine;
using System.CommandLine.Parsing;
using ZB.MOM.WW.ScadaBridge.CLI.Commands;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;

namespace ZB.MOM.WW.ScadaBridge.CLI.Tests.Commands;

/// <summary>
/// Tests for <see cref="CachedCallCommands"/> — pins that option parsing maps to the
/// correct management command objects with the correct field values.
/// </summary>
public class CachedCallCommandsTests
{
    private static readonly Option<string> Url = new("--url") { Recursive = true };
    private static readonly Option<string> Username = new("--username") { Recursive = true };
    private static readonly Option<string> Password = new("--password") { Recursive = true };
    private static readonly Option<string> Format = CliOptions.CreateFormatOption();

    private static Command BuildCachedCall()
        => CachedCallCommands.Build(Url, Format, Username, Password);

    // ── retry ──────────────────────────────────────────────────────────────

    [Fact]
    public void Retry_WithSiteIdAndMessageId_ProducesCorrectCommand()
    {
        var group = BuildCachedCall();
        var retry = group.Subcommands.Single(c => c.Name == "retry");

        var result = retry.Parse(
            ["--site-id", "site-a", "--tracked-operation-id", "11111111-2222-3333-4444-555555555555"]);

        Assert.Empty(result.Errors);
        var cmd = CachedCallCommands.BuildRetryCommand(result);

        Assert.Equal("site-a", cmd.SiteIdentifier);
        Assert.Equal("11111111-2222-3333-4444-555555555555", cmd.MessageId);
    }

    [Fact]
    public void Retry_SiteIdAndTrackedOperationId_AreRequired()
    {
        var group = BuildCachedCall();
        var retry = group.Subcommands.Single(c => c.Name == "retry");

        var siteId = retry.Options.Single(o => o.Name == "--site-id");
        var opId = retry.Options.Single(o => o.Name == "--tracked-operation-id");

        Assert.True(siteId.Required, "--site-id must be required.");
        Assert.True(opId.Required, "--tracked-operation-id must be required.");
    }

    [Fact]
    public void Retry_MissingOptions_ProducesParseErrors()
    {
        var group = BuildCachedCall();
        var retry = group.Subcommands.Single(c => c.Name == "retry");

        var result = retry.Parse([]);

        Assert.NotEmpty(result.Errors);
    }

    // ── discard ────────────────────────────────────────────────────────────

    [Fact]
    public void Discard_WithSiteIdAndMessageId_ProducesCorrectCommand()
    {
        var group = BuildCachedCall();
        var discard = group.Subcommands.Single(c => c.Name == "discard");

        var result = discard.Parse(
            ["--site-id", "site-b", "--tracked-operation-id", "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"]);

        Assert.Empty(result.Errors);
        var cmd = CachedCallCommands.BuildDiscardCommand(result);

        Assert.Equal("site-b", cmd.SiteIdentifier);
        Assert.Equal("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", cmd.MessageId);
    }

    [Fact]
    public void Discard_SiteIdAndTrackedOperationId_AreRequired()
    {
        var group = BuildCachedCall();
        var discard = group.Subcommands.Single(c => c.Name == "discard");

        var siteId = discard.Options.Single(o => o.Name == "--site-id");
        var opId = discard.Options.Single(o => o.Name == "--tracked-operation-id");

        Assert.True(siteId.Required, "--site-id must be required.");
        Assert.True(opId.Required, "--tracked-operation-id must be required.");
    }

    [Fact]
    public void Discard_MissingOptions_ProducesParseErrors()
    {
        var group = BuildCachedCall();
        var discard = group.Subcommands.Single(c => c.Name == "discard");

        var result = discard.Parse([]);

        Assert.NotEmpty(result.Errors);
    }

    // ── group shape ────────────────────────────────────────────────────────

    [Fact]
    public void CachedCallGroup_HasRetryAndDiscard()
    {
        var group = BuildCachedCall();
        var subNames = group.Subcommands.Select(c => c.Name).ToHashSet();

        Assert.Contains("retry", subNames);
        Assert.Contains("discard", subNames);
    }

    [Fact]
    public void CachedCallGroup_Name_IsCachedCall()
    {
        var group = BuildCachedCall();
        Assert.Equal("cached-call", group.Name);
    }

    // ── registry round-trip ────────────────────────────────────────────────

    [Fact]
    public void RetryParkedMessageCommand_ResolvesViaRegistry()
    {
        var name = ManagementCommandRegistry.GetCommandName(typeof(RetryParkedMessageCommand));
        Assert.False(string.IsNullOrWhiteSpace(name));
        Assert.Equal(typeof(RetryParkedMessageCommand), ManagementCommandRegistry.Resolve(name));
    }

    [Fact]
    public void DiscardParkedMessageCommand_ResolvesViaRegistry()
    {
        var name = ManagementCommandRegistry.GetCommandName(typeof(DiscardParkedMessageCommand));
        Assert.False(string.IsNullOrWhiteSpace(name));
        Assert.Equal(typeof(DiscardParkedMessageCommand), ManagementCommandRegistry.Resolve(name));
    }
}
