/**
 * Per-key token bucket for outbound marketplace calls (process-local, PM2
 * instances: 1). Amazon's Listings Items usage plan is 5 req/s per
 * seller+app with burst 5 — the flush runs at 4 req/s per order source so a
 * concurrent interactive push never tips it into 429s.
 */
const buckets = new Map<string, { tokens: number; last: number; queue: Promise<void> }>()

export interface BucketOpts { ratePerSec: number; burst: number }

const sleep = (ms: number) => new Promise(r => setTimeout(r, ms))

export const acquire = async (key: string, opts: BucketOpts): Promise<void> => {
  let b = buckets.get(key)
  if (!b) { b = { tokens: opts.burst, last: Date.now(), queue: Promise.resolve() }; buckets.set(key, b) }
  const bucket = b
  // serialize waiters per key so the arithmetic below is never interleaved
  const my = bucket.queue.then(async () => {
    for (;;) {
      const now = Date.now()
      bucket.tokens = Math.min(opts.burst, bucket.tokens + ((now - bucket.last) / 1000) * opts.ratePerSec)
      bucket.last = now
      if (bucket.tokens >= 1) { bucket.tokens -= 1; return }
      await sleep(Math.ceil(((1 - bucket.tokens) / opts.ratePerSec) * 1000))
    }
  })
  bucket.queue = my.catch(() => {})
  await my
}

export const LISTINGS_WRITE_BUCKET: BucketOpts = { ratePerSec: 4, burst: 4 }
export const LISTINGS_READ_BUCKET: BucketOpts = { ratePerSec: 4, burst: 4 }
