package com.foxx.androidcast.network.transport;

/*********************************************************************
 * NackRateLimiter.java
 * Created at: Mon 18 May 2026 14:25:02 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class NackRateLimiter
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 53 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	fb01ac8cf405797ee3f320822e701f6c0b401a7f75c221b92eb132cd4aaa9b3e
**********************************************************************/
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/** Limits outbound NACK storms per message id. */
public final class NackRateLimiter {
    private static final long MIN_INTERVAL_MS = 40L;
    private static final int MAX_NACKS_PER_MESSAGE = 6;
    private static final long ENTRY_TTL_MS = 30_000L;

    private final Map<Integer, Entry> entries = new ConcurrentHashMap<>();

    public boolean allow(int messageId) {
        long now = System.currentTimeMillis();
        pruneExpired(now);
        Entry e = entries.computeIfAbsent(messageId, id -> new Entry());
        synchronized (e) {
            if (e.count >= MAX_NACKS_PER_MESSAGE) {
                return false;
            }
            if (now - e.lastRequestMs < MIN_INTERVAL_MS) {
                return false;
            }
            e.lastRequestMs = now;
            e.count++;
            return true;
        }
    }

    public void pruneExpired(long nowMs) {
        Iterator<Map.Entry<Integer, Entry>> it = entries.entrySet().iterator();
        while (it.hasNext()) {
            Entry e = it.next().getValue();
            if (nowMs - e.lastRequestMs > ENTRY_TTL_MS) {
                it.remove();
            }
        }
    }

    private static final class Entry {
        long lastRequestMs;
        int count;
    }
}
