using System.Collections.Concurrent;

namespace NATS.Server.JetStream.Publish;

public sealed class PublishPreconditions
{
    private readonly ConcurrentDictionary<string, DedupeEntry> _dedupe = new(StringComparer.Ordinal);

    public bool IsDuplicate(string? msgId, int duplicateWindowMs, out ulong existingSequence)
    {
        existingSequence = 0;
        if (string.IsNullOrEmpty(msgId))
            return false;

        if (!_dedupe.TryGetValue(msgId, out var entry))
            return false;

        if (duplicateWindowMs > 0
            && DateTime.UtcNow - entry.TimestampUtc > TimeSpan.FromMilliseconds(duplicateWindowMs))
        {
            _dedupe.TryRemove(msgId, out _);
            return false;
        }

        existingSequence = entry.Sequence;
        return true;
    }

    public void Record(string? msgId, ulong sequence)
    {
        if (string.IsNullOrEmpty(msgId))
            return;

        _dedupe[msgId] = new DedupeEntry(sequence, DateTime.UtcNow);
    }

    public void TrimOlderThan(int duplicateWindowMs)
    {
        if (duplicateWindowMs <= 0)
            return;

        var cutoff = DateTime.UtcNow.AddMilliseconds(-duplicateWindowMs);
        foreach (var (key, entry) in _dedupe)
        {
            if (entry.TimestampUtc < cutoff)
                _dedupe.TryRemove(key, out _);
        }
    }

    public bool CheckExpectedLastSeq(ulong expectedLastSeq, ulong actualLastSeq)
        => expectedLastSeq == 0 || expectedLastSeq == actualLastSeq;

    private readonly record struct DedupeEntry(ulong Sequence, DateTime TimestampUtc);
}
