using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using ZB.MOM.WW.LocalDb;

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

/// <summary>
/// A real <see cref="ILocalDb"/> over a temp file, for tests that construct a
/// <see cref="SiteEventLogger"/> directly.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="SiteEventLogger"/> takes <see cref="ILocalDb"/> rather than a connection
/// string, and there is no in-memory mode — <c>LocalDbOptions.Path</c> is a filesystem
/// path. A real one is used rather than a stub on purpose: connections handed out by
/// <c>ILocalDb</c> carry the <c>zb_hlc_next()</c> UDF that <c>site_events</c>' capture
/// triggers call, so a stub returning a bare <c>SqliteConnection</c> would pass these
/// tests while failing closed the moment the table is registered for replication.
/// </para>
/// <para>
/// No <c>onReady</c> callback and no <c>RegisterReplicated</c>: the logger's own
/// <c>InitializeSchema</c> creates the table, which is what keeps a directly-constructed
/// logger self-sufficient. Registration is the host's job
/// (<c>SiteLocalDbSetup.OnReady</c>).
/// </para>
/// </remarks>
public sealed class TestLocalDb : IDisposable
{
    private readonly ServiceProvider _provider;

    private TestLocalDb(ServiceProvider provider, ILocalDb db)
    {
        _provider = provider;
        Db = db;
    }

    /// <summary>The live local database.</summary>
    public ILocalDb Db { get; }

    /// <summary>Opens a local database at <paramref name="databasePath"/>.</summary>
    public static TestLocalDb Create(string databasePath)
    {
        var configuration = new ConfigurationBuilder()
            .AddInMemoryCollection(new Dictionary<string, string?>
            {
                ["LocalDb:Path"] = databasePath,
            })
            .Build();

        var provider = new ServiceCollection()
            .AddZbLocalDb(configuration)
            .BuildServiceProvider();

        return new TestLocalDb(provider, provider.GetRequiredService<ILocalDb>());
    }

    /// <summary>
    /// Deletes <paramref name="databasePath"/> and its WAL sidecars. Call only after the
    /// owning <see cref="TestLocalDb"/> is disposed — the master connection anchors the WAL.
    /// </summary>
    public static void DeleteFiles(string databasePath)
    {
        foreach (var suffix in new[] { "", "-wal", "-shm" })
        {
            try { File.Delete(databasePath + suffix); } catch { /* best effort */ }
        }
    }

    public void Dispose() => _provider.Dispose();
}
