using System.IO;
using System.Text;

namespace SharepointToolbox.Services.Export;

/// <summary>
/// Central file-write plumbing for export services so every CSV and HTML
/// artefact gets a consistent encoding: CSV files are written with a UTF-8
/// BOM (required for Excel to detect the encoding when opening a
/// double-clicked .csv), HTML files are written without a BOM (some browsers
/// and iframe <c>srcdoc</c> paths render the BOM as a visible character).
/// Export services should call these helpers rather than constructing
/// <see cref="UTF8Encoding"/> inline.
/// </summary>
internal static class ExportFileWriter
{
    private static readonly UTF8Encoding Utf8WithBom = new(encoderShouldEmitUTF8Identifier: true);
    private static readonly UTF8Encoding Utf8NoBom   = new(encoderShouldEmitUTF8Identifier: false);

    /// <summary>Writes <paramref name="csv"/> to <paramref name="filePath"/> as UTF-8 with BOM.</summary>
    public static Task WriteCsvAsync(string filePath, string csv, CancellationToken ct)
        => File.WriteAllTextAsync(filePath, csv, Utf8WithBom, ct);

    /// <summary>Writes <paramref name="html"/> to <paramref name="filePath"/> as UTF-8 without BOM.</summary>
    public static Task WriteHtmlAsync(string filePath, string html, CancellationToken ct)
        => File.WriteAllTextAsync(filePath, html, Utf8NoBom, ct);

    /// <summary>
    /// Streams a <see cref="StringBuilder"/> directly to disk as UTF-8 with
    /// BOM, chunk by chunk. Avoids the full-document <c>ToString()</c> copy
    /// and the separate UTF-8 byte buffer that <see cref="File.WriteAllTextAsync(string, string, Encoding, CancellationToken)"/>
    /// would otherwise allocate — meaningful for large CSV exports.
    /// </summary>
    public static Task WriteCsvChunksAsync(string filePath, StringBuilder builder, CancellationToken ct)
        => WriteChunksAsync(filePath, builder, Utf8WithBom, ct);

    /// <summary>
    /// Streams a <see cref="StringBuilder"/> directly to disk as UTF-8 without
    /// BOM. Same rationale as <see cref="WriteCsvChunksAsync"/> — for large
    /// HTML reports it halves peak memory by skipping the intermediate string.
    /// </summary>
    public static Task WriteHtmlChunksAsync(string filePath, StringBuilder builder, CancellationToken ct)
        => WriteChunksAsync(filePath, builder, Utf8NoBom, ct);

    private static async Task WriteChunksAsync(string filePath, StringBuilder builder, Encoding encoding, CancellationToken ct)
    {
        // FileOptions.Asynchronous lets StreamWriter use true async I/O.
        await using var fs = new FileStream(
            filePath,
            FileMode.Create,
            FileAccess.Write,
            FileShare.None,
            bufferSize: 64 * 1024,
            options: FileOptions.Asynchronous | FileOptions.SequentialScan);
        await using var sw = new StreamWriter(fs, encoding, bufferSize: 64 * 1024);

        foreach (var chunk in builder.GetChunks())
        {
            ct.ThrowIfCancellationRequested();
            await sw.WriteAsync(chunk, ct);
        }
        await sw.FlushAsync(ct);
    }
}
