/**
 * Cache Service - Valkey/Redis operations
 */

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

class CacheService {
  private client: Redis | null = null;
  private isConnected = false;

  /**
   * Initialize Redis connection
   */
  async connect(): Promise<void> {
    if (this.client && this.isConnected) return;

    this.client = new Redis({
      host: env.REDIS_HOST,
      port: env.REDIS_PORT,
      password: env.REDIS_PASSWORD || undefined,
      retryStrategy: (times) => {
        if (times > 3) {
          console.error('[Cache] Max retry attempts reached');
          return null;
        }
        return Math.min(times * 200, 2000);
      },
      maxRetriesPerRequest: 3,
    });

    this.client.on('connect', () => {
      this.isConnected = true;
      console.log('[Cache] Connected to Redis/Valkey');
    });

    this.client.on('error', (err) => {
      console.error('[Cache] Redis error:', err.message);
      this.isConnected = false;
    });

    this.client.on('close', () => {
      this.isConnected = false;
      console.log('[Cache] Redis connection closed');
    });
  }

  /**
   * Get value from cache
   */
  async get<T>(key: string): Promise<T | null> {
    if (!this.client) {
      console.warn('[Cache] Not connected, returning null');
      return null;
    }

    try {
      const value = await this.client.get(key);
      if (!value) return null;
      return JSON.parse(value) as T;
    } catch (err) {
      console.error(`[Cache] Get error for key ${key}:`, err);
      return null;
    }
  }

  /**
   * Set value in cache with optional TTL
   */
  async set<T>(key: string, value: T, ttlSeconds?: number): Promise<boolean> {
    if (!this.client) {
      console.warn('[Cache] Not connected, skipping set');
      return false;
    }

    try {
      const serialized = JSON.stringify(value);
      if (ttlSeconds) {
        await this.client.setex(key, ttlSeconds, serialized);
      } else {
        await this.client.set(key, serialized);
      }
      return true;
    } catch (err) {
      console.error(`[Cache] Set error for key ${key}:`, err);
      return false;
    }
  }

  /**
   * Delete key from cache
   */
  async del(key: string): Promise<boolean> {
    if (!this.client) return false;

    try {
      await this.client.del(key);
      return true;
    } catch (err) {
      console.error(`[Cache] Delete error for key ${key}:`, err);
      return false;
    }
  }

  /**
   * Delete keys matching pattern
   */
  async delPattern(pattern: string): Promise<number> {
    if (!this.client) return 0;

    try {
      const keys = await this.client.keys(pattern);
      if (keys.length === 0) return 0;
      return await this.client.del(...keys);
    } catch (err) {
      console.error(`[Cache] Delete pattern error for ${pattern}:`, err);
      return 0;
    }
  }

  /**
   * Check if connected
   */
  get connected(): boolean {
    return this.isConnected;
  }

  /**
   * Add to set
   */
  async sadd(key: string, member: string): Promise<boolean> {
    if (!this.client) return false;
    try {
      await this.client.sadd(key, member);
      return true;
    } catch (err) {
      console.error(`[Cache] SADD error for key ${key}:`, err);
      return false;
    }
  }

  /**
   * Remove from set
   */
  async srem(key: string, member: string): Promise<boolean> {
    if (!this.client) return false;
    try {
      await this.client.srem(key, member);
      return true;
    } catch (err) {
      console.error(`[Cache] SREM error for key ${key}:`, err);
      return false;
    }
  }

  /**
   * Get all members of set
   */
  async smembers(key: string): Promise<string[]> {
    if (!this.client) return [];
    try {
      return await this.client.smembers(key);
    } catch (err) {
      console.error(`[Cache] SMEMBERS error for key ${key}:`, err);
      return [];
    }
  }

  /**
   * Disconnect from Redis
   */
  async disconnect(): Promise<void> {
    if (this.client) {
      await this.client.quit();
      this.client = null;
      this.isConnected = false;
    }
  }
}

export const cacheService = new CacheService();
