import { createClient, RedisClientType } from 'redis'
import { Request } from 'express'

export interface RateLimitConfig {
  interval: number
  limit: number
}

export interface RateLimitResult {
  success: boolean
  limit: number
  remaining: number
  reset: number
}

export class RateLimit {
  private redis: RedisClientType
  private prefix: string

  constructor() {
    this.redis = createClient({
      url: process.env.REDIS_URL || 'redis://localhost:6379'
    })
    this.prefix = 'ratelimit'
  }

  private async ensureConnected() {
    if (!this.redis.isOpen) {
      await this.redis.connect()
    }
  }

  private getIP(req: Request): string {
    return req.ip || req.socket.remoteAddress || 'unknown'
  }

  private getKey(identifier: string): string {
    return `${this.prefix}:${identifier}`
  }

  async check(req: Request, config: RateLimitConfig): Promise<RateLimitResult> {
    await this.ensureConnected()
    
    const ip = this.getIP(req)
    const key = this.getKey(ip)
    const now = Math.floor(Date.now() / 1000)
    const windowStart = now - (now % config.interval)
    const windowKey = `${key}:${windowStart}`

    const multi = this.redis.multi()
    multi.incr(windowKey)
    multi.expire(windowKey, config.interval)
    
    const results = await multi.exec()
    const count = Number(results[0]) || 1
    const remaining = Math.max(0, config.limit - count)

    return {
      success: count <= config.limit,
      limit: config.limit,
      remaining,
      reset: windowStart + config.interval
    }
  }
}

export async function rateLimit(req: Request, config: RateLimitConfig = { interval: 60 * 60, limit: 5 }): Promise<RateLimitResult> {
  try {
    const limiter = new RateLimit()
    return await limiter.check(req, config)
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : 'Unknown error'
    console.warn('Redis rate limiting failed, allowing request:', errorMessage)
    // Fallback: allow the request when Redis is unavailable
    return {
      success: true,
      limit: config.limit,
      remaining: config.limit - 1,
      reset: Math.floor(Date.now() / 1000) + config.interval
    }
  }
}
