using MBN_STOCK_WEBVIEW.Infrastructure;
using MMoneyCoderSharp.Data;
using MySqlConnector;
using Oracle.ManagedDataAccess.Client;

namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;

public sealed class ProviderDatabaseConnectionFactoryTests
{
    [Fact]
    public void OracleCommand_AcceptsLogicalNameForColonPlaceholder()
    {
        using var command = new OracleCommand("SELECT :code FROM DUAL");
        var parameter = command.CreateParameter();
        parameter.ParameterName = "code";
        parameter.Value = 7;

        command.Parameters.Add(parameter);

        Assert.Equal(0, command.Parameters.IndexOf("code"));
    }

    [Fact]
    public void MariaDbCommand_NormalizesLogicalNameForAtPlaceholder()
    {
        using var command = new MySqlCommand("SELECT @code");
        var parameter = command.CreateParameter();
        parameter.ParameterName = "code";
        parameter.Value = 7;

        command.Parameters.Add(parameter);

        Assert.Equal(0, command.Parameters.IndexOf("code"));
        Assert.Equal(0, command.Parameters.IndexOf("@code"));
    }

    [Fact]
    public void CreateOracleConnection_UsesSidDescriptor()
    {
        var options = TestOptions.CreateConfigured();
        options.Oracle.Sid = "TESTSID";
        options.Oracle.ServiceName = null;
        var factory = new ProviderDatabaseConnectionFactory(options);

        using var connection = factory.Create(DataSourceKind.Oracle);
        var parsed = new OracleConnectionStringBuilder(connection.ConnectionString);

        Assert.True(parsed.DataSource.Contains("(SID=TESTSID)", StringComparison.Ordinal));
        Assert.False(parsed.DataSource.Contains("SERVICE_NAME", StringComparison.Ordinal));
        Assert.False(parsed.PersistSecurityInfo);
    }

    [Fact]
    public void CreateOracleConnection_UsesServiceNameDescriptor()
    {
        var options = TestOptions.CreateConfigured();
        options.Oracle.Sid = null;
        options.Oracle.ServiceName = "TESTSERVICE";
        var factory = new ProviderDatabaseConnectionFactory(options);

        using var connection = factory.Create(DataSourceKind.Oracle);
        var parsed = new OracleConnectionStringBuilder(connection.ConnectionString);

        Assert.True(parsed.DataSource.Contains("(SERVICE_NAME=TESTSERVICE)", StringComparison.Ordinal));
        Assert.False(parsed.DataSource.Contains("(SID=", StringComparison.Ordinal));
        Assert.False(parsed.PersistSecurityInfo);
    }

    [Theory]
    [InlineData(MariaDbTlsMode.Disabled, MySqlSslMode.None)]
    [InlineData(MariaDbTlsMode.Preferred, MySqlSslMode.Preferred)]
    [InlineData(MariaDbTlsMode.Required, MySqlSslMode.Required)]
    [InlineData(MariaDbTlsMode.VerifyCertificate, MySqlSslMode.VerifyCA)]
    [InlineData(MariaDbTlsMode.VerifyIdentity, MySqlSslMode.VerifyFull)]
    public void CreateMariaDbConnection_UsesEndpointPortAndTls(
        MariaDbTlsMode configuredTls,
        MySqlSslMode expectedTls)
    {
        var options = TestOptions.CreateConfigured();
        options.MariaDb.Host = "maria-builder-test.invalid";
        options.MariaDb.Port = 43306;
        options.MariaDb.TlsMode = configuredTls;
        var factory = new ProviderDatabaseConnectionFactory(options);

        using var connection = factory.Create(DataSourceKind.MariaDb);
        var parsed = new MySqlConnectionStringBuilder(connection.ConnectionString);

        Assert.Equal("maria-builder-test.invalid", parsed.Server);
        Assert.Equal(43306u, parsed.Port);
        Assert.Equal(expectedTls, parsed.SslMode);
        Assert.False(parsed.PersistSecurityInfo);
    }

    [Fact]
    public void PublicConfigurationException_DoesNotExposeRejectedHostUserOrPassword()
    {
        const string rejectedHost = "rejected(host)-sensitive-sentinel";
        const string user = "oracle-user-sensitive-sentinel";
        const string password = "oracle-password-sensitive-sentinel";
        var options = TestOptions.CreateConfigured();
        options.Oracle.Host = rejectedHost;
        options.Oracle.UserName = user;
        options.Oracle.Password = password;
        var factory = new ProviderDatabaseConnectionFactory(options);

        var exception = Assert.Throws<DatabaseConfigurationException>(
            () => factory.Create(DataSourceKind.Oracle));

        Assert.DoesNotContain(rejectedHost, exception.Message, StringComparison.Ordinal);
        Assert.DoesNotContain(user, exception.Message, StringComparison.Ordinal);
        Assert.DoesNotContain(password, exception.Message, StringComparison.Ordinal);
    }
}
