using System.Text.Json;
using System.Text.Json.Serialization;

namespace ERPCore.Services.Production;

/// <summary>
/// The one serializer for every jsonb column in the manufacturing module —
/// <c>template_stages.field_defs</c>, <c>run_stages.field_defs</c>/<c>field_values</c>
/// and <c>run_stage_events.payload</c>.
/// </summary>
/// <remarks>
/// <para>Those columns hold a pre-serialized <c>string</c> rather than a mapped POCO,
/// following the <c>AuditLog.ChangeSet</c> precedent. Beyond consistency this keeps
/// <c>AuditScribe</c> honest: an owned/typed jsonb mapping would surface the nested
/// objects as their own change-tracker entries and scatter spurious audit rows across
/// the new tables.</para>
/// <para>Everything is written through here so a column can only ever contain canonical
/// JSON — the raw client string is never stored, which means a malformed
/// <c>fieldDefs</c> can't reach the database and can't later break a run created from
/// that template.</para>
/// </remarks>
public static class ProductionJson
{
    /// <summary>Matches the API's own wire format (docs/11 §1.3): camelCase, enums as strings.</summary>
    public static readonly JsonSerializerOptions Options = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        Converters = { new JsonStringEnumConverter() }
    };

    public static string Serialize<T>(T value) => JsonSerializer.Serialize(value, Options);

    /// <summary>
    /// Reads a stored column back. Returns <paramref name="fallback"/> for null/blank so a
    /// caller never has to null-check; a parse failure is a genuine data-integrity problem
    /// and is allowed to throw.
    /// </summary>
    public static T Deserialize<T>(string? json, T fallback)
        => string.IsNullOrWhiteSpace(json) ? fallback : JsonSerializer.Deserialize<T>(json, Options) ?? fallback;
}
