import Redis from 'ioredis';

// Redis/Valkey connection instance
let redisClient: Redis | null = null;

/**
 * Get or create Redis connection
 * Uses REDIS_URL from environment or defaults to localhost
 */
export function getRedisClient(): Redis {
  if (!redisClient) {
    const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
    
    redisClient = new Redis(redisUrl, {
      maxRetriesPerRequest: 3,
      retryStrategy(times) {
        const delay = Math.min(times * 50, 2000);
        return delay;
      },
      reconnectOnError(err) {
        const targetErrors = ['READONLY', 'ECONNRESET', 'ETIMEDOUT'];
        return targetErrors.some(targetError => err.message.includes(targetError));
      }
    });

    redisClient.on('error', (err) => {
      console.error('Redis connection error:', { message: err.message });
    });

    redisClient.on('connect', () => {
      console.log('Redis connected successfully');
    });
  }

  return redisClient;
}

/**
 * Cache helper functions
 */

interface CacheOptions {
  ttl?: number; // Time to live in seconds (default: 300 = 5 minutes)
}

/**
 * Get cached data by key
 */
export async function getCached<T>(key: string): Promise<T | null> {
  try {
    const redis = getRedisClient();
    const data = await redis.get(key);
    
    if (!data) {
      return null;
    }
    
    return JSON.parse(data) as T;
  } catch (err) {
    console.error('Cache get error:', { key, message: (err as Error).message });
    return null;
  }
}

/**
 * Set cached data with optional TTL
 */
export async function setCached(key: string, data: any, options: CacheOptions = {}): Promise<boolean> {
  try {
    const redis = getRedisClient();
    const ttl = options.ttl || 300; // Default 5 minutes
    const serialized = JSON.stringify(data);
    
    await redis.setex(key, ttl, serialized);
    return true;
  } catch (err) {
    console.error('Cache set error:', { key, message: (err as Error).message });
    return false;
  }
}

/**
 * Delete cached data by key or pattern
 */
export async function deleteCached(keyOrPattern: string): Promise<number> {
  try {
    const redis = getRedisClient();
    
    // If it contains wildcards, use scan + delete
    if (keyOrPattern.includes('*')) {
      let cursor = '0';
      let deletedCount = 0;
      
      do {
        const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', keyOrPattern, 'COUNT', 100);
        cursor = nextCursor;
        
        if (keys.length > 0) {
          const deleted = await redis.del(...keys);
          deletedCount += deleted;
        }
      } while (cursor !== '0');
      
      return deletedCount;
    } else {
      // Direct key deletion
      return await redis.del(keyOrPattern);
    }
  } catch (err) {
    console.error('Cache delete error:', { keyOrPattern, message: (err as Error).message });
    return 0;
  }
}

/**
 * Search functionality using Redis
 */

export interface SearchOptions {
  fields?: string[]; // Fields to search in
  limit?: number; // Max results
  fuzzy?: boolean; // Enable fuzzy matching
}

/**
 * Cache search results
 */
export async function cacheSearchResults(query: string, results: any[], options: CacheOptions = {}): Promise<boolean> {
  const cacheKey = `search:${query.toLowerCase().trim()}`;
  return setCached(cacheKey, results, options);
}

/**
 * Get cached search results
 */
export async function getCachedSearchResults(query: string): Promise<any[] | null> {
  const cacheKey = `search:${query.toLowerCase().trim()}`;
  return getCached<any[]>(cacheKey);
}

/**
 * Invalidate all search caches
 */
export async function invalidateSearchCache(): Promise<number> {
  return deleteCached('search:*');
}

/**
 * Close Redis connection (for cleanup)
 */
export async function closeRedisConnection(): Promise<void> {
  if (redisClient) {
    await redisClient.quit();
    redisClient = null;
  }
}
