using System.CommandLine;
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 the <c>scadabridge notification smtp update</c> subcommand. The command
/// gained optional flags — <c>--tls-mode</c>, <c>--credentials</c> and <c>--transport</c> —
/// that plumb through to <see cref="UpdateSmtpConfigCommand"/>. These tests pin that the
/// flags parse, are genuinely optional (non-breaking), that <c>--tls-mode</c> rejects
/// values outside the canonical {None, StartTLS, SSL} set, and that <c>--transport</c>
/// rejects values outside {Smtp, Ews}.
/// </summary>
public class SmtpUpdateCommandTests
{
    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 SmtpUpdateCommand()
    {
        var notification = NotificationCommands.Build(Url, Format, Username, Password);
        var smtp = notification.Subcommands.Single(c => c.Name == "smtp");
        return smtp.Subcommands.Single(c => c.Name == "update");
    }

    private static ParseResult ParseUpdate(params string[] args)
        => SmtpUpdateCommand().Parse(args);

    [Fact]
    public void Update_WithTlsModeAndCredentials_ProducesCommandCarryingThem()
    {
        var parse = ParseUpdate(
            "--id", "1", "--server", "smtp.example.com", "--port", "587",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com",
            "--tls-mode", "None", "--credentials", "user:pass");

        Assert.Empty(parse.Errors);
        var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse);

        Assert.Equal(1, cmd.SmtpConfigId);
        Assert.Equal("smtp.example.com", cmd.Server);
        Assert.Equal(587, cmd.Port);
        Assert.Equal("Basic", cmd.AuthMode);
        Assert.Equal("noreply@example.com", cmd.FromAddress);
        Assert.Equal("None", cmd.TlsMode);
        Assert.Equal("user:pass", cmd.Credentials);
    }

    [Fact]
    public void Update_WithoutTlsModeAndCredentials_ProducesCommandWithNulls()
    {
        var parse = ParseUpdate(
            "--id", "2", "--server", "smtp.example.com", "--port", "25",
            "--auth-mode", "OAuth2", "--from-address", "noreply@example.com");

        Assert.Empty(parse.Errors);
        var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse);

        Assert.Equal(2, cmd.SmtpConfigId);
        Assert.Null(cmd.TlsMode);
        Assert.Null(cmd.Credentials);
    }

    [Theory]
    [InlineData("None")]
    [InlineData("StartTLS")]
    [InlineData("SSL")]
    public void Update_TlsModeOption_AcceptsCanonicalValues(string value)
    {
        var parse = ParseUpdate(
            "--id", "1", "--server", "smtp.example.com", "--port", "587",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com",
            "--tls-mode", value);

        Assert.Empty(parse.Errors);
    }

    [Theory]
    [InlineData("Bogus")]
    [InlineData("tls")]
    [InlineData("none")] // AcceptOnlyFromAmong is case-sensitive: constrain to canonical spelling
    public void Update_TlsModeOption_RejectsValuesOutsideCanonicalSet(string value)
    {
        var parse = ParseUpdate(
            "--id", "1", "--server", "smtp.example.com", "--port", "587",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com",
            "--tls-mode", value);

        Assert.NotEmpty(parse.Errors);
    }

    [Fact]
    public void Update_WithTransportEws_ProducesCommandCarryingIt()
    {
        var parse = ParseUpdate(
            "--id", "1", "--server", "https://mail.example.com/ews/exchange.asmx", "--port", "443",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com",
            "--transport", "Ews");

        Assert.Empty(parse.Errors);
        var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse);

        Assert.Equal("Ews", cmd.Transport);
    }

    [Fact]
    public void Update_WithoutTransport_ProducesCommandWithNullTransport()
    {
        var parse = ParseUpdate(
            "--id", "2", "--server", "smtp.example.com", "--port", "25",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com");

        Assert.Empty(parse.Errors);
        var cmd = NotificationCommands.BuildUpdateSmtpConfigCommand(parse);

        Assert.Null(cmd.Transport);
    }

    [Theory]
    [InlineData("Graph")]
    [InlineData("ews")] // AcceptOnlyFromAmong is case-sensitive: constrain to canonical spelling
    public void Update_TransportOption_RejectsValuesOutsideCanonicalSet(string value)
    {
        var parse = ParseUpdate(
            "--id", "1", "--server", "smtp.example.com", "--port", "587",
            "--auth-mode", "Basic", "--from-address", "noreply@example.com",
            "--transport", value);

        Assert.NotEmpty(parse.Errors);
    }

    [Fact]
    public void Update_TlsModeAndCredentials_AreNotRequired()
    {
        var update = SmtpUpdateCommand();
        var tls = update.Options.Single(o => o.Name == "--tls-mode");
        var creds = update.Options.Single(o => o.Name == "--credentials");

        Assert.False(tls.Required, "--tls-mode must be optional (preserve-if-omitted).");
        Assert.False(creds.Required, "--credentials must be optional (preserve-if-omitted).");
    }
}
