using System.Collections.Immutable;
using System.Linq;
using System.Text.Json.Serialization;

namespace StellaOps.Feedser.Models;

/// <summary>
/// Describes the origin of a canonical field and how/when it was captured.
/// </summary>
public sealed record AdvisoryProvenance
{
    public static AdvisoryProvenance Empty { get; } = new("unknown", "unspecified", string.Empty, DateTimeOffset.UnixEpoch);

    [JsonConstructor]
    public AdvisoryProvenance(
        string source,
        string kind,
        string value,
        string? decisionReason,
        DateTimeOffset recordedAt,
        ImmutableArray<string> fieldMask)
        : this(source, kind, value, recordedAt, fieldMask.IsDefault ? null : fieldMask.AsEnumerable(), decisionReason)
    {
    }

    public AdvisoryProvenance(
        string source,
        string kind,
        string value,
        DateTimeOffset recordedAt,
        IEnumerable<string>? fieldMask = null,
        string? decisionReason = null)
    {
        Source = Validation.EnsureNotNullOrWhiteSpace(source, nameof(source));
        Kind = Validation.EnsureNotNullOrWhiteSpace(kind, nameof(kind));
        Value = Validation.TrimToNull(value);
        DecisionReason = Validation.TrimToNull(decisionReason);
        RecordedAt = recordedAt.ToUniversalTime();
        FieldMask = NormalizeFieldMask(fieldMask);
    }

    public string Source { get; }

    public string Kind { get; }

    public string? Value { get; }

    public string? DecisionReason { get; }

    public DateTimeOffset RecordedAt { get; }

    public ImmutableArray<string> FieldMask { get; }

    private static ImmutableArray<string> NormalizeFieldMask(IEnumerable<string>? fieldMask)
    {
        if (fieldMask is null)
        {
            return ImmutableArray<string>.Empty;
        }

        var buffer = fieldMask
            .Where(static value => !string.IsNullOrWhiteSpace(value))
            .Select(static value => value.Trim().ToLowerInvariant())
            .Distinct(StringComparer.Ordinal)
            .OrderBy(static value => value, StringComparer.Ordinal)
            .ToImmutableArray();

        return buffer.IsDefault ? ImmutableArray<string>.Empty : buffer;
    }
}
