import Redis from 'ioredis';
import { TimeManager } from '../utils/time.utils.js';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';

// Get directory name in ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Load environment variables again (redundant but safe)
const envPath = path.resolve(__dirname, '../.env');
console.log('[RedisService] Loading .env file from:', envPath);
dotenv.config({ path: envPath });

export class RedisService {
  constructor() {
    this.timeManager = new TimeManager();
    this.DEFAULT_TTL = 5 * 60; // 5 minutes default TTL
    this.isConnected = false;
    this._initializeRedis();
  }

  _initializeRedis() {
    try {
      // Debug: Print all environment variables we're looking for
      console.log('[RedisService] Environment variables state:', {
        REDIS_HOST: process.env.REDIS_HOST ? '(set)' : '(not set)',
        REDIS_PORT: process.env.REDIS_PORT ? '(set)' : '(not set)',
        REDIS_USERNAME: process.env.REDIS_USERNAME ? '(set)' : '(not set)',
        REDIS_PASSWORD: process.env.REDIS_PASSWORD ? '(set)' : '(not set)',
      });

      // Log Redis configuration (without password)
      const host = process.env.REDIS_HOST || 'localhost';
      const port = parseInt(process.env.REDIS_PORT) || 6379;
      const username = process.env.REDIS_USERNAME || 'default';
      const password = process.env.REDIS_PASSWORD;

      console.log('[RedisService] Initializing Redis with config:', {
        host,
        port,
        username,
        hasPassword: !!password
      });

      const config = {
        host,
        port,
        username,
        retryStrategy: (times) => {
          const delay = Math.min(times * 50, 2000);
          return delay;
        },
        maxRetriesPerRequest: 3,
        enableReadyCheck: true,
        connectTimeout: 10000,
        showFriendlyErrorStack: true,
        retryUnfulfilled: true,
        maxRetryAttempts: 5
      };

      // Only add password if it exists
      if (password) {
        console.log('[RedisService] Adding password to config');
        config.password = password;
      } else {
        console.warn('[RedisService] No Redis password found in environment variables!');
      }

      this.client = new Redis(config);

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

      this.client.on('ready', () => {
        console.log('[RedisService] Redis is ready');
        this.isConnected = true;
      });

      this.client.on('error', (err) => {
        console.error('[RedisService] Redis error:', err);
        this.isConnected = false;
        // Log more details about the error
        if (err.code === 'WRONGPASS') {
          console.error('[RedisService] Authentication failed. Please check your Redis password.');
        }
      });

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

      this.client.on('reconnecting', (params) => {
        console.log('[RedisService] Reconnecting to Redis:', params);
      });

    } catch (error) {
      console.error('[RedisService] Error initializing Redis:', error);
      this.isConnected = false;
    }
  }

  async get(key) {
    if (!this.isConnected) {
      return null;
    }
    
    try {
      const data = await this.client.get(key);
      return data ? JSON.parse(data) : null;
    } catch (error) {
      console.error('[RedisService] Error getting data:', error);
      return null;
    }
  }

  async set(key, data, ttl = this.DEFAULT_TTL) {
    if (!this.isConnected) {
      return;
    }
    
    try {
      await this.client.setex(key, ttl, JSON.stringify(data));
    } catch (error) {
      console.error('[RedisService] Error setting data:', error);
    }
  }

// Helper to generate cache keys
_getCacheKey(type, params = {}) {
  const {
    timeRange,
    startDate,
    endDate,
    metricId,
    metric,
    daily,
    cacheKey,
    isPreviousPeriod
  } = params;

  let key = `klaviyo:${type}`;

  // Handle "stats:details" for daily or metric-based keys
  if (type === 'stats:details') {
    key += `:${metric}${daily ? ':daily' : ''}`;
  }

  // If a specific cache key is provided, use it (highest priority)
  if (cacheKey) {
    key += `:${cacheKey}`;
  } 
  // Otherwise, build a default cache key
  else if (timeRange) {
    key += `:${timeRange}${metricId ? `:${metricId}` : ''}`;
    if (isPreviousPeriod) {
      key += ':prev'; 
    }
  } else if (startDate && endDate) {
    key += `:custom:${startDate}:${endDate}${metricId ? `:${metricId}` : ''}`;
    if (isPreviousPeriod) {
      key += ':prev'; 
    }
  }

  return key;
}


  // Get TTL based on time range
  _getTTL(timeRange) {
    const TTL_MAP = {
      'today': 2 * 60, // 2 minutes
      'yesterday': 30 * 60, // 30 minutes
      'thisWeek': 5 * 60, // 5 minutes
      'lastWeek': 60 * 60, // 1 hour
      'thisMonth': 10 * 60, // 10 minutes
      'lastMonth': 2 * 60 * 60, // 2 hours
      'last7days': 5 * 60, // 5 minutes
      'last30days': 15 * 60, // 15 minutes
      'custom': 15 * 60 // 15 minutes
    };
    return TTL_MAP[timeRange] || this.DEFAULT_TTL;
  }

  async getEventData(type, params) {
    if (!this.isConnected) {
      return null;
    }
    
    try {
      const baseKey = this._getCacheKey('events', params);
      const data = await this.get(`${baseKey}:${type}`);
      return data;
    } catch (error) {
      console.error('[RedisService] Error getting event data:', error);
      return null;
    }
  }

  async cacheEventData(type, params, data) {
    if (!this.isConnected) {
      return;
    }
    
    try {
      const ttl = this._getTTL(params.timeRange);
      const baseKey = this._getCacheKey('events', params);

      // Cache raw event data
      await this.set(`${baseKey}:${type}`, data, ttl);
    } catch (error) {
      console.error('[RedisService] Error caching event data:', error);
    }
  }

  async clearCache(params = {}) {
    if (!this.isConnected) {
      return;
    }
    
    try {
      const pattern = this._getCacheKey('events', params) + '*';
      const keys = await this.client.keys(pattern);
      if (keys.length > 0) {
        await this.client.del(...keys);
      }
    } catch (error) {
      console.error('[RedisService] Error clearing cache:', error);
    }
  }
} 