using System.Text.Json;
using MBN_STOCK_WEBVIEW.Infrastructure;
using MMoneyCoderSharp.Data;

namespace MBN_STOCK_WEBVIEW.Infrastructure.Tests;

public sealed class DatabaseOptionsLoaderTests
{
    [Fact]
    public void Load_MissingFile_ReturnsSafeUnconfiguredDefaults()
    {
        var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"), "missing.json");
        var loader = new DatabaseOptionsLoader(_ => null);

        var options = loader.Load(path);

        Assert.False(options.IsConfigured(DataSourceKind.Oracle));
        Assert.False(options.IsConfigured(DataSourceKind.MariaDb));
        Assert.Equal(1521, options.Oracle.Port);
        Assert.Equal(3306, options.MariaDb.Port);
        Assert.Null(options.Oracle.Password);
        Assert.Null(options.MariaDb.Password);
    }

    [Fact]
    public async Task LoadAsync_EnvironmentValuesOverrideJsonValues()
    {
        var directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(directory);
        var path = Path.Combine(directory, "database.local.json");

        try
        {
            var jsonOptions = new DatabaseOptions
            {
                Oracle = new OracleDatabaseOptions
                {
                    Host = "json-oracle-host.invalid",
                    Port = 1521,
                    Sid = "JSONSID",
                    UserName = "json-oracle-user",
                    Password = "json-oracle-password"
                },
                MariaDb = new MariaDbDatabaseOptions
                {
                    Host = "json-maria-host.invalid",
                    Port = 3306,
                    Database = "json_database",
                    UserName = "json-maria-user",
                    Password = "json-maria-password",
                    TlsMode = MariaDbTlsMode.Preferred
                },
                Resilience = new DatabaseResilienceOptions
                {
                    OperationTimeoutSeconds = 45,
                    MaximumRetryCount = 2,
                    InitialRetryDelayMilliseconds = 250
                }
            };
            await File.WriteAllTextAsync(path, JsonSerializer.Serialize(jsonOptions));

            var environment = new Dictionary<string, string>(StringComparer.Ordinal)
            {
                ["MBN_STOCK_ORACLE_HOST"] = "env-oracle-host.invalid",
                ["MBN_STOCK_ORACLE_PORT"] = "1522",
                ["MBN_STOCK_ORACLE_USERNAME"] = "env-oracle-user",
                ["MBN_STOCK_ORACLE_PASSWORD"] = "env-oracle-password",
                ["MBN_STOCK_MARIADB_HOST"] = "env-maria-host.invalid",
                ["MBN_STOCK_MARIADB_PORT"] = "3307",
                ["MBN_STOCK_MARIADB_DATABASE"] = "env_database",
                ["MBN_STOCK_MARIADB_USERNAME"] = "env-maria-user",
                ["MBN_STOCK_MARIADB_PASSWORD"] = "env-maria-password",
                ["MBN_STOCK_MARIADB_TLS_MODE"] = "Required",
                ["MBN_STOCK_DB_OPERATION_TIMEOUT_SECONDS"] = "12",
                ["MBN_STOCK_DB_MAX_RETRY_COUNT"] = "4",
                ["MBN_STOCK_DB_INITIAL_RETRY_DELAY_MS"] = "75"
            };
            var loader = new DatabaseOptionsLoader(
                name => environment.TryGetValue(name, out var value) ? value : null);

            var options = await loader.LoadAsync(path);

            Assert.Equal("env-oracle-host.invalid", options.Oracle.Host);
            Assert.Equal(1522, options.Oracle.Port);
            Assert.Equal("JSONSID", options.Oracle.Sid);
            Assert.Equal("env-oracle-user", options.Oracle.UserName);
            Assert.Equal("env-oracle-password", options.Oracle.Password);
            Assert.Equal("env-maria-host.invalid", options.MariaDb.Host);
            Assert.Equal(3307, options.MariaDb.Port);
            Assert.Equal("env_database", options.MariaDb.Database);
            Assert.Equal("env-maria-user", options.MariaDb.UserName);
            Assert.Equal("env-maria-password", options.MariaDb.Password);
            Assert.Equal(MariaDbTlsMode.Required, options.MariaDb.TlsMode);
            Assert.Equal(12, options.Resilience.OperationTimeoutSeconds);
            Assert.Equal(4, options.Resilience.MaximumRetryCount);
            Assert.Equal(75, options.Resilience.InitialRetryDelayMilliseconds);
        }
        finally
        {
            Directory.Delete(directory, recursive: true);
        }
    }

    [Fact]
    public void ToString_DoesNotExposeHostUserOrPassword()
    {
        const string oracleHost = "oracle-host-sensitive-sentinel";
        const string oracleUser = "oracle-user-sensitive-sentinel";
        const string oraclePassword = "oracle-password-sensitive-sentinel";
        const string mariaHost = "maria-host-sensitive-sentinel";
        const string mariaUser = "maria-user-sensitive-sentinel";
        const string mariaPassword = "maria-password-sensitive-sentinel";
        var options = TestOptions.CreateConfigured();
        options.Oracle.Host = oracleHost;
        options.Oracle.UserName = oracleUser;
        options.Oracle.Password = oraclePassword;
        options.MariaDb.Host = mariaHost;
        options.MariaDb.UserName = mariaUser;
        options.MariaDb.Password = mariaPassword;

        var representations = string.Join(
            Environment.NewLine,
            options.ToString(),
            options.Oracle.ToString(),
            options.MariaDb.ToString(),
            options.Resilience.ToString());

        Assert.DoesNotContain(oracleHost, representations, StringComparison.Ordinal);
        Assert.DoesNotContain(oracleUser, representations, StringComparison.Ordinal);
        Assert.DoesNotContain(oraclePassword, representations, StringComparison.Ordinal);
        Assert.DoesNotContain(mariaHost, representations, StringComparison.Ordinal);
        Assert.DoesNotContain(mariaUser, representations, StringComparison.Ordinal);
        Assert.DoesNotContain(mariaPassword, representations, StringComparison.Ordinal);
    }
}
