import Redis from 'ioredis'

// Konfiguration
const REDIS_HOST = process.env.REDIS_HOST || 'localhost'
const REDIS_PORT = parseInt(process.env.REDIS_PORT || '6379')
const REDIS_DB = parseInt(process.env.REDIS_DB || '0')
const REDIS_ENABLED = process.env.REDIS_ENABLED !== 'false' // Standard: aktiviert

// Singleton Pattern
let redisClient: Redis | null = null
let redisAvailable: boolean | null = null
let connectionChecked = false

function createRedisClient(): Redis {
  const client = new Redis({
    host: REDIS_HOST,
    port: REDIS_PORT,
    db: REDIS_DB,
    password: process.env.REDIS_PASSWORD || undefined,
    maxRetriesPerRequest: 3,
    retryStrategy: (times) => {
      if (times > 3) {
        console.warn('[Redis] Max retries erreicht, verwende In-Memory-Fallback')
        return null // Stoppe Retry-Versuche
      }
      return Math.min(times * 100, 2000)
    },
    lazyConnect: true,
    enableOfflineQueue: false, // Keine Queuing wenn offline
  })

  client.on('error', (err) => {
    if (redisAvailable !== false) {
      console.error('[Redis] Connection error:', err.message)
      redisAvailable = false
    }
  })

  client.on('connect', () => {
    console.log(`[Redis] Connected to ${REDIS_HOST}:${REDIS_PORT}`)
    redisAvailable = true
  })

  client.on('ready', () => {
    redisAvailable = true
  })

  client.on('close', () => {
    if (redisAvailable !== false) {
      console.warn('[Redis] Connection closed')
    }
  })

  return client
}

export const getRedis = (): Redis => {
  if (!redisClient) {
    redisClient = createRedisClient()
  }
  return redisClient
}

/**
 * Gibt den Redis-Client zurück oder null wenn nicht verfügbar/deaktiviert
 */
export const getRedisClient = (): Redis | null => {
  if (!REDIS_ENABLED) {
    return null
  }

  try {
    return getRedis()
  } catch {
    return null
  }
}

/**
 * Prüft ob Redis verfügbar ist (mit echtem Connection-Check beim ersten Aufruf)
 */
export const isRedisAvailable = (): boolean => {
  if (!REDIS_ENABLED) {
    return false
  }

  // Wenn bereits gecheckt und Status bekannt
  if (connectionChecked && redisAvailable !== null) {
    return redisAvailable
  }

  // Initiale Prüfung - optimistisch true, wird bei Fehler auf false gesetzt
  return true
}

/**
 * Führt einen echten Connection-Check durch und gibt das Ergebnis zurück
 */
export const checkRedisConnection = async (): Promise<boolean> => {
  if (!REDIS_ENABLED) {
    redisAvailable = false
    connectionChecked = true
    return false
  }

  try {
    const redis = getRedis()
    await redis.ping()
    redisAvailable = true
    connectionChecked = true
    console.log('[Redis] Connection verified successfully')
    return true
  } catch (error) {
    redisAvailable = false
    connectionChecked = true
    console.warn('[Redis] Connection check failed:', (error as Error).message)
    return false
  }
}

/**
 * Setzt den Redis-Verfügbarkeitsstatus (wird bei Fehlern aufgerufen)
 */
export const setRedisAvailable = (available: boolean): void => {
  redisAvailable = available
  connectionChecked = true
}

// Cache Helper Funktionen
export const cache = {
  async get<T>(key: string): Promise<T | null> {
    try {
      const redis = getRedis()
      const data = await redis.get(key)
      if (data) {
        console.log(`[Cache] HIT: ${key}`)
        return JSON.parse(data)
      }
      console.log(`[Cache] MISS: ${key}`)
      return null
    } catch (error) {
      console.error('[Cache] GET Error:', error)
      return null
    }
  },

  async set(key: string, value: unknown, ttlSeconds = 300): Promise<void> {
    try {
      const redis = getRedis()
      await redis.setex(key, ttlSeconds, JSON.stringify(value))
      console.log(`[Cache] SET: ${key} (TTL: ${ttlSeconds}s)`)
    } catch (error) {
      console.error('[Cache] SET Error:', error)
    }
  },

  async del(key: string): Promise<void> {
    try {
      const redis = getRedis()
      await redis.del(key)
      console.log(`[Cache] DEL: ${key}`)
    } catch (error) {
      console.error('[Cache] DEL Error:', error)
    }
  },

  async delPattern(pattern: string): Promise<void> {
    try {
      const redis = getRedis()
      const keys = await redis.keys(pattern)
      if (keys.length > 0) {
        await redis.del(...keys)
        console.log(`[Cache] DEL Pattern: ${pattern} (${keys.length} keys)`)
      }
    } catch (error) {
      console.error('[Cache] DEL Pattern Error:', error)
    }
  },

  async flush(): Promise<void> {
    try {
      const redis = getRedis()
      await redis.flushdb()
      console.log('[Cache] FLUSH: Database cleared')
    } catch (error) {
      console.error('[Cache] FLUSH Error:', error)
    }
  },

  // Cache-Wrapper für einfache Verwendung
  async wrap<T>(
    key: string,
    fn: () => Promise<T>,
    ttlSeconds = 300
  ): Promise<T> {
    const cached = await this.get<T>(key)
    if (cached !== null) {
      return cached
    }

    const result = await fn()
    await this.set(key, result, ttlSeconds)
    return result
  },
}

export default getRedis
