using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace StellaOps.Feedser.Exporter.Json;

internal static class JsonExportManifestWriter
{
    private static readonly JsonSerializerOptions SerializerOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        WriteIndented = true,
    };

    public static async Task WriteAsync(
        JsonExportResult result,
        string digest,
        string exporterVersion,
        CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(result);
        ArgumentException.ThrowIfNullOrEmpty(digest);
        ArgumentException.ThrowIfNullOrEmpty(exporterVersion);

        var exportId = Path.GetFileName(result.ExportDirectory);
        var files = result.Files
            .Select(static file => new JsonExportManifestFile(file.RelativePath.Replace("\\", "/", StringComparison.Ordinal), file.Length, file.Digest))
            .ToArray();

        var manifest = new JsonExportManifest(
            exportId,
            result.ExportedAt.UtcDateTime,
            digest,
            result.AdvisoryCount,
            result.TotalBytes,
            files.Length,
            files,
            exporterVersion);

        var payload = JsonSerializer.SerializeToUtf8Bytes(manifest, SerializerOptions);
        var manifestPath = Path.Combine(result.ExportDirectory, "manifest.json");
        await File.WriteAllBytesAsync(manifestPath, payload, cancellationToken).ConfigureAwait(false);
        File.SetLastWriteTimeUtc(manifestPath, result.ExportedAt.UtcDateTime);
    }

    private sealed record JsonExportManifest(
        [property: JsonPropertyOrder(1)] string ExportId,
        [property: JsonPropertyOrder(2)] DateTime GeneratedAt,
        [property: JsonPropertyOrder(3)] string Digest,
        [property: JsonPropertyOrder(4)] int AdvisoryCount,
        [property: JsonPropertyOrder(5)] long TotalBytes,
        [property: JsonPropertyOrder(6)] int FileCount,
        [property: JsonPropertyOrder(7)] IReadOnlyList<JsonExportManifestFile> Files,
        [property: JsonPropertyOrder(8)] string ExporterVersion);

    private sealed record JsonExportManifestFile(
        [property: JsonPropertyOrder(1)] string Path,
        [property: JsonPropertyOrder(2)] long Bytes,
        [property: JsonPropertyOrder(3)] string Digest);
}
