import Redis from 'ioredis'
import { env } from '../../config/env'
import { logger } from '../../config/logger'

class DragonflyClient {
  private client: Redis
  private readonly ttl: number

  constructor() {
    this.ttl = parseInt(env.DRAGONFLY_TTL_SECONDS, 10)

    this.client = new Redis(env.DRAGONFLY_URL, {
      // null = não limita tentativas por comando; a reconexão é gerenciada pelo retryStrategy
      maxRetriesPerRequest: null,
      enableReadyCheck: false,
      lazyConnect: true,
      // Falha imediata quando desconectado — evita fila infinita em memória
      enableOfflineQueue: false,
      // Reconexão exponencial: 200ms → 400ms → ... → 30s
      retryStrategy(times: number) {
        const delay = Math.min(200 * 2 ** times, 30_000)
        return delay
      },
    })

    this.client.on('connect', () => logger.info('DragonflyDB conectado'))
    this.client.on('ready',   () => logger.info('DragonflyDB pronto'))
    this.client.on('error',   (err) => logger.error({ msg: 'DragonflyDB erro', code: (err as any).code }))
    this.client.on('close',   () => logger.warn('DragonflyDB conexão fechada — reconectando...'))
  }

  /** true se o cliente está conectado e pronto */
  isAlive(): boolean {
    return this.client.status === 'ready'
  }

  async connect(): Promise<void> {
    // Inicia conexão em background — não bloqueia o startup se Redis estiver fora
    // retryStrategy cuida da reconexão automática
    this.client.connect().catch(() => { /* reconexão em background via retryStrategy */ })
  }

  async get(key: string): Promise<string | null> {
    try { return await this.client.get(key) } catch { return null }
  }

  async getJson<T>(key: string): Promise<T | null> {
    try {
      const raw = await this.client.get(key)
      if (!raw) return null
      return JSON.parse(raw) as T
    } catch { return null }
  }

  async set(key: string, value: string, ttlSeconds?: number): Promise<void> {
    try {
      const ttl = ttlSeconds ?? this.ttl
      if (ttl > 0) {
        await this.client.set(key, value, 'EX', ttl)
      } else {
        await this.client.set(key, value)
      }
    } catch { /* Redis indisponível — ignora silenciosamente */ }
  }

  async setJson(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
    try {
      const ttl = ttlSeconds ?? this.ttl
      if (ttl > 0) {
        await this.client.set(key, JSON.stringify(value), 'EX', ttl)
      } else {
        await this.client.set(key, JSON.stringify(value))
      }
    } catch { /* Redis indisponível — ignora silenciosamente */ }
  }

  async psetex(key: string, ttlMs: number, value: string): Promise<void> {
    try { await this.client.psetex(key, ttlMs, value) } catch { }
  }

  async del(key: string): Promise<void> {
    try { await this.client.del(key) } catch { }
  }

  async exists(key: string): Promise<boolean> {
    try {
      const count = await this.client.exists(key)
      return count > 0
    } catch { return false }
  }

  /** Publica em um canal pub/sub (usado para broadcasting de QR/status) */
  async publish(channel: string, message: string): Promise<void> {
    try { await this.client.publish(channel, message) } catch { }
  }

  raw(): Redis {
    return this.client
  }
}

export const dragonfly = new DragonflyClient()
