using Microsoft.Data.Sqlite;

namespace ZB.MOM.WW.MxGateway.Tests.Security.Authentication;

/// <summary>
/// Disposable temporary directory for SQLite auth-store tests. Each instance owns a
/// unique directory under <c>%TEMP%</c>; <see cref="Dispose"/> clears SQLite connection
/// pools (which otherwise keep the <c>.db</c> file handle open) and deletes the directory
/// so test runs do not leak temp files or open handles.
/// </summary>
internal sealed class TempDatabaseDirectory : IDisposable
{
    private bool _disposed;

    private TempDatabaseDirectory(string path)
    {
        Path = path;
    }

    /// <summary>Gets the path to the temporary directory.</summary>
    public string Path { get; }

    /// <summary>Creates a new uniquely named temporary directory under the given prefix.</summary>
    /// <param name="prefix">Folder name placed under <c>%TEMP%</c> to group related test directories.</param>
    /// <returns>The created temporary directory handle.</returns>
    public static TempDatabaseDirectory Create(string prefix)
    {
        string path = System.IO.Path.Combine(
            System.IO.Path.GetTempPath(),
            prefix,
            Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(path);

        return new TempDatabaseDirectory(path);
    }

    /// <summary>Returns a database file path inside this temporary directory.</summary>
    /// <param name="fileName">Database file name; defaults to the gateway auth database name.</param>
    /// <returns>The full path to the database file.</returns>
    public string DatabasePath(string fileName = "gateway-auth.db")
    {
        return System.IO.Path.Combine(Path, fileName);
    }

    /// <summary>Clears pooled SQLite connections and deletes the temporary directory.</summary>
    public void Dispose()
    {
        if (_disposed)
        {
            return;
        }

        _disposed = true;

        // Microsoft.Data.Sqlite pools connections by default; clear the pools so the
        // underlying file handle is released before the directory is deleted.
        SqliteConnection.ClearAllPools();

        try
        {
            if (Directory.Exists(Path))
            {
                Directory.Delete(Path, recursive: true);
            }
        }
        catch (IOException)
        {
            // Best-effort cleanup; a transient handle should not fail the test.
        }
        catch (UnauthorizedAccessException)
        {
            // Best-effort cleanup; a transient handle should not fail the test.
        }
    }
}
