using System.Text;
using MailKit.Security;

namespace ZB.MOM.WW.ScadaBridge.NotificationService.Tests;

/// <summary>
/// NS-016: <see cref="MailKitSmtpClientWrapper.AuthenticateAsync"/> must never
/// silently skip authentication for a misconfigured SMTP config — a missing
/// credential, an unrecognised auth type, or an unparseable Basic credential
/// must be a hard, surfaced error rather than an unauthenticated send.
/// NS-021: the OAuth2 (XOAUTH2) branch must carry a non-empty user identity
/// (the SMTP From address) — an empty user is rejected by M365 with `535 5.7.3`.
/// </summary>
public class MailKitSmtpClientWrapperTests
{
    [Fact]
    public async Task Authenticate_EmptyCredentials_Throws()
    {
        // An AuthType of "basic"/"oauth2" with a null/empty Credentials value is a
        // misconfigured row; the wrapper used to "return" and send unauthenticated.
        var wrapper = new MailKitSmtpClientWrapper();

        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("basic", null));
        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("oauth2", ""));
    }

    [Fact]
    public async Task Authenticate_UnknownAuthType_Throws()
    {
        // The switch had cases only for "basic"/"oauth2" and no default — any other
        // value (typo, future "ntlm") fell through and sent unauthenticated.
        var wrapper = new MailKitSmtpClientWrapper();

        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("ntlm", "user:pass"));
    }

    [Fact]
    public async Task Authenticate_BasicCredentialWithoutColon_Throws()
    {
        // A "basic" credential string that does not split into exactly two parts was
        // silently skipped — the connection then sent unauthenticated.
        var wrapper = new MailKitSmtpClientWrapper();

        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("basic", "nocolon"));
    }

    [Fact]
    public async Task Authenticate_OAuth2WithoutUserName_Throws()
    {
        // NS-021: passing an OAuth2 access token but no user identity (FromAddress)
        // used to construct `new SaslMechanismOAuth2("", credentials)`, which M365
        // rejects with `535 5.7.3`. The wrapper now refuses upfront so the caller
        // sees a clean configuration error rather than a confusing server reject.
        var wrapper = new MailKitSmtpClientWrapper();

        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("oauth2", "access-token", oauth2UserName: null));
        await Assert.ThrowsAsync<SmtpPermanentException>(
            () => wrapper.AuthenticateAsync("oauth2", "access-token", oauth2UserName: ""));
    }

    [Fact]
    public void XOAuth2InitialResponse_CarriesUserAndBearer()
    {
        // NS-021 regression guard: independent of the wrapper, prove that MailKit's
        // SaslMechanismOAuth2 puts `user=<userName>` into the initial-response bytes
        // — i.e. wiring the wrapper to pass `FromAddress` is sufficient to fix the
        // M365 handshake. If MailKit ever changes the framing this test will catch it.
        var sasl = new SaslMechanismOAuth2("noreply@example.com", "tok-xyz");

        var initial = sasl.Challenge(string.Empty);
        var asString = Encoding.UTF8.GetString(Convert.FromBase64String(initial));

        Assert.Contains("user=noreply@example.com", asString);
        Assert.Contains("auth=Bearer tok-xyz", asString);
    }
}
