package ratelimit

import "sync"

// Rejection aggregates the limiter rejections of one key within one report
// window. Class is the limiter class (user, public, email or admin); Key is the
// class-specific subject — an account id for the user class, a client IP for the
// others. The JSON shape is the gateway→backend rate-limit report wire contract.
type Rejection struct {
	Class    string `json:"class"`
	Key      string `json:"key"`
	Rejected int    `json:"rejected"`
}

// Tracker accumulates limiter rejections between drains. The gateway's periodic
// reporter drains it to emit the per-key log summary and the backend report; the
// per-rejection cost is one map increment under a mutex, safe on the hot path.
type Tracker struct {
	mu sync.Mutex
	m  map[trackerKey]int
}

type trackerKey struct{ class, key string }

// NewTracker constructs an empty Tracker.
func NewTracker() *Tracker {
	return &Tracker{m: make(map[trackerKey]int)}
}

// Add counts one rejection of key under class.
func (t *Tracker) Add(class, key string) {
	t.mu.Lock()
	defer t.mu.Unlock()
	t.m[trackerKey{class: class, key: key}]++
}

// Drain returns the rejections accumulated since the previous drain, in
// unspecified order, and resets the tracker. It returns nil when nothing was
// rejected.
func (t *Tracker) Drain() []Rejection {
	t.mu.Lock()
	defer t.mu.Unlock()
	if len(t.m) == 0 {
		return nil
	}
	out := make([]Rejection, 0, len(t.m))
	for k, n := range t.m {
		out = append(out, Rejection{Class: k.class, Key: k.key, Rejected: n})
	}
	clear(t.m)
	return out
}
