namespace ZB.MOM.WW.Audit;

/// <summary>
/// Redactor that caps oversized <see cref="AuditEvent.DetailsJson"/> and <see cref="AuditEvent.Target"/>.
/// Never throws — over-redacts (drops both DetailsJson and Target) on internal failure. The
/// secret-field policy (which fields are sensitive) stays per-project; compose this with a project
/// redactor as needed.
/// </summary>
public sealed class TruncatingAuditRedactor : IAuditRedactor
{
    private readonly TruncatingAuditRedactorOptions _options;

    /// <summary>Creates the redactor with the given options (defaults when null).</summary>
    public TruncatingAuditRedactor(TruncatingAuditRedactorOptions? options = null)
        => _options = options ?? new TruncatingAuditRedactorOptions();

    /// <inheritdoc />
    public AuditEvent Apply(AuditEvent rawEvent)
    {
        try
        {
            return rawEvent with
            {
                Target = Truncate(rawEvent.Target, _options.MaxTargetLength),
                DetailsJson = Truncate(rawEvent.DetailsJson, _options.MaxDetailsJsonLength),
            };
        }
        catch
        {
            // Hard contract: never throw, and over-redact to a STRICTLY safer event on internal
            // failure — scrub every field this redactor owns (both DetailsJson and Target).
            return rawEvent with { DetailsJson = null, Target = null };
        }
    }

    private string? Truncate(string? value, int max)
    {
        if (max < 0) max = 0; // clamp nonsensical negative caps so a config bug fails safe, not throws
        if (value is null || value.Length <= max) return value;
        var marker = _options.TruncationMarker;
        if (marker.Length >= max) return marker[..max];
        return string.Concat(value.AsSpan(0, max - marker.Length), marker);
    }
}
