namespace NATS.Server.JetStream.Storage;

/// <summary>
/// Provides crash-safe atomic file writes using the write-to-temp-then-rename pattern.
/// Each call writes to a uniquely-named sibling temp file (<c>{path}.{random}.tmp</c>),
/// flushes to disk, then atomically renames over the target path so that:
/// <list type="bullet">
///   <item>A partial write never leaves a corrupt target file.</item>
///   <item>Concurrent callers to different paths never collide on temp files.</item>
/// </list>
///
/// Reference: golang/nats-server/server/filestore.go:10599 (_writeFullState)
/// </summary>
public static class AtomicFileWriter
{
    /// <summary>
    /// Writes <paramref name="data"/> atomically to <paramref name="path"/>.
    /// The data is written to a unique <c>{path}.{random}.tmp</c> sibling, flushed,
    /// then renamed over <paramref name="path"/> with overwrite semantics.
    /// </summary>
    public static async Task WriteAtomicallyAsync(string path, byte[] data)
    {
        var tmpPath = path + "." + Path.GetRandomFileName() + ".tmp";
        await using (var fs = new FileStream(tmpPath, FileMode.CreateNew, FileAccess.Write, FileShare.None,
                         bufferSize: 4096, useAsync: true))
        {
            await fs.WriteAsync(data);
            await fs.FlushAsync();
        }
        File.Move(tmpPath, path, overwrite: true);
    }

    /// <summary>
    /// Writes <paramref name="data"/> atomically to <paramref name="path"/>.
    /// The data is written to a unique <c>{path}.{random}.tmp</c> sibling, flushed,
    /// then renamed over <paramref name="path"/> with overwrite semantics.
    /// </summary>
    public static async Task WriteAtomicallyAsync(string path, ReadOnlyMemory<byte> data)
    {
        var tmpPath = path + "." + Path.GetRandomFileName() + ".tmp";
        await using (var fs = new FileStream(tmpPath, FileMode.CreateNew, FileAccess.Write, FileShare.None,
                         bufferSize: 4096, useAsync: true))
        {
            await fs.WriteAsync(data);
            await fs.FlushAsync();
        }
        File.Move(tmpPath, path, overwrite: true);
    }
}
