import type { NextApiRequest, NextApiResponse } from "next"

import LRU from 'lru-cache'
type optionsType = { uniqueTokenPerInterval?: string | number, interval?: string | number, ip?: boolean, token?: string, limit?: number }
type checkType = { limit?: number, token?: string, req: NextApiRequest }
type limiterReturn = {
  check(options: checkType): Promise<Boolean>
  remove(options: { token: string }): Promise<Boolean>
}
const rateLimit = (options: optionsType): limiterReturn => {
  const tokenCache = new LRU({
    max: parseInt((options.uniqueTokenPerInterval || 500).toString(), 10),
    // maxAge: parseInt((options.interval || 60000).toString(), 10),
    ttl: parseInt((options.interval || 60000).toString(), 10),
  })
  return {
    check: ({ limit = (options.limit || 0), token = (options.token || ""), req }: checkType): Promise<Boolean> =>
      new Promise<Boolean>((resolve) => {
        const tokenStr = `${token || options.token}${options.ip ? `-${getIp(req)}` : ""}`
        const maxLimit: number | string = limit || options.limit || 0
        const tokenCount: undefined | Array<any> = tokenCache.get(tokenStr) || [0]
        if (tokenCount[0] === 0) {
          tokenCache.set(tokenStr, tokenCount)
        }
        tokenCount[0] += 1
        const currentUsage = tokenCount[0]
        const isRateLimited = currentUsage > parseInt(maxLimit.toString(), 10)
        console.log(tokenStr)
        return isRateLimited ? resolve(true) : resolve(false)
      }),
    remove: ({ token = (options.token || "") }: { token: string }): Promise<Boolean> =>
      new Promise<Boolean>((resolve) => {
        return resolve(tokenCache.delete(token))
      }),
  }
}
export const getIp = (request: NextApiRequest & {
  ip?: string
}) =>
  request.ip ||
  request.headers['x-forwarded-for'] ||
  request.headers['x-real-ip'] ||
  request.connection.remoteAddress


export const bhApiLimiter = rateLimit({
  interval: 90 * 1000, // 90 seconds
  uniqueTokenPerInterval: 500, // Max 500 users per second
  ip: true,
  token: "bhapi",
  limit: 5
  // 5 requests to brawlhalla api per 90 seconds
})
export const trackerLimiter = rateLimit({
  interval: 4 * 60 * 60 * 1000, // 4 hours
  uniqueTokenPerInterval: 1000, // Max 500 users per second
  ip: true,
  token: "tracker",
  limit: 1
})
export const commentsLimiter = rateLimit({
  interval: 4 * 60 * 60 * 1000, // 4 hours
  uniqueTokenPerInterval: 1000, // Max 500 users per second
  ip: false,
  token: "comments",
  limit: 2
})
export const rateLimitMessage = (res?: NextApiResponse) => res ? res.json({
  error: true,
  message: "Your requests are being rate limited! Please try again in a bit"
}) : {
  error: true,
  message: "Your requests are being rate limited! Please try again in a bit"
}
export const IsRateLimited = async (req: NextApiRequest, limiter: limiterReturn, token?: string, limit?: number): Promise<Boolean> => await limiter.check({
  req,
  token: token,
  limit
})
export const removeToken = async (limiter: limiterReturn, token: string,): Promise<Boolean> => await limiter.remove({
  token: token,
})
export default rateLimit
