/**
 * Rate limiting via Redis (ioredis)
 * Fallback : compteur en mémoire si Redis indisponible (dev local sans Docker)
 *
 * Usage :
 *   const allowed = await checkRateLimit(ip, 'submit', 3)
 *   if (!allowed) throw createError({ statusCode: 429, ... })
 */

import Redis from 'ioredis'

let redisClient: Redis | null = null
let redisConnected = false

// Compteur en mémoire (fallback si Redis KO)
const memoryCounters: Map<string, { count: number; resetAt: number }> = new Map()

function getRedisClient(): Redis | null {
  if (redisClient !== null) return redisConnected ? redisClient : null

  try {
    const config = useRuntimeConfig()
    const url = config.redisUrl || 'redis://127.0.0.1:6379'
    redisClient = new Redis(url, {
      lazyConnect: true,
      enableOfflineQueue: false,
      maxRetriesPerRequest: 1,
      connectTimeout: 2000,
    })

    redisClient.on('connect', () => {
      redisConnected = true
      console.log('[rateLimit] Redis connecté')
    })

    redisClient.on('error', (err) => {
      redisConnected = false
      console.warn('[rateLimit] Redis erreur — fallback mémoire activé:', err.message)
    })

    redisClient.connect().catch(() => {
      redisConnected = false
    })

    return redisConnected ? redisClient : null
  } catch (e) {
    console.warn('[rateLimit] Impossible de créer le client Redis — fallback mémoire')
    return null
  }
}

/**
 * Vérifie et incrémente le compteur d'appels.
 * @param ip        Adresse IP du client
 * @param action    Clé d'action (ex : 'submit', 'comment')
 * @param maxPerDay Nombre max d'appels autorisés par jour
 * @returns true si autorisé, false si limite dépassée
 */
export async function checkRateLimit(
  ip: string,
  action: string,
  maxPerDay: number,
): Promise<boolean> {
  const key = `ratelimit:${action}:${ip}:${todayKey()}`

  const client = getRedisClient()

  if (client && redisConnected) {
    try {
      const count = await client.incr(key)
      if (count === 1) {
        // Expire à minuit (secondes restantes dans la journée)
        await client.expireat(key, tomorrowMidnightUnix())
      }
      return count <= maxPerDay
    } catch (e) {
      console.warn('[rateLimit] Redis incr échoué — fallback mémoire:', (e as Error).message)
    }
  }

  // Fallback mémoire
  return memoryRateLimit(key, maxPerDay)
}

// ── Helpers ────────────────────────────────────────────────────────────────────

function todayKey(): string {
  const d = new Date()
  return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}-${String(d.getUTCDate()).padStart(2, '0')}`
}

function tomorrowMidnightUnix(): number {
  const d = new Date()
  d.setUTCHours(24, 0, 0, 0)
  return Math.floor(d.getTime() / 1000)
}

function memoryRateLimit(key: string, maxPerDay: number): boolean {
  const now = Date.now()
  const entry = memoryCounters.get(key)

  if (!entry || entry.resetAt <= now) {
    memoryCounters.set(key, { count: 1, resetAt: tomorrowMidnightUnix() * 1000 })
    return true
  }

  entry.count++
  return entry.count <= maxPerDay
}
