import { createClient } from "redis";

// Global Redis client that persists between function invocations
let redisClient: any = null;
let redisPublisherClient: any = null;
let connectionAttempts = 0;
let isInitialized = false;
let pingIntervalId: NodeJS.Timeout | null = null;

// Clean the Redis URL if it contains variable name or quotes
const cleanRedisUrl = (url: string | undefined): string | undefined => {
  if (!url) return undefined;
  
  // If the URL contains KV_URL= or REDIS_URL=, extract just the URL part
  if (url.includes('KV_URL=') || url.includes('REDIS_URL=')) {
    const match = url.match(/(?:KV_URL=|REDIS_URL=)["']?(rediss?:\/\/[^"']+)["']?/);
    return match ? match[1] : url;
  }
  
  // Remove any surrounding quotes
  return url.replace(/^["'](.+)["']$/, '$1');
};

// Initialize Redis client if not already initialized
export async function getRedisClient() {
  if (redisClient && await isRedisConnected(redisClient)) {
    console.log("Using existing Redis client");
    connectionAttempts = 0;
    return redisClient;
  }
  
  console.log("Creating new Redis client");
  connectionAttempts++;
  
  const redisUrl = cleanRedisUrl(process.env.REDIS_URL) || cleanRedisUrl(process.env.KV_URL);
  if (!redisUrl) {
    throw new Error("REDIS_URL or KV_URL environment variable is not set");
  }
  
  // Create Redis client with maximum persistence settings
  redisClient = createClient({
    url: redisUrl,
    socket: {
      reconnectStrategy: (retries) => {
        const delay = Math.min(Math.pow(1.5, retries) * 100, 5000);
        console.log(`Redis reconnecting in ${delay}ms (attempt ${retries})`);
        return delay;
      },
      connectTimeout: 30000,
      keepAlive: 5000,
      noDelay: true,
      tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined,
    },
    pingInterval: 1000,
    disableOfflineQueue: false,
    commandTimeout: 5000,
    retryStrategy: () => 1000, // Retry every second
    autoResubscribe: true,
    autoResendUnfulfilledCommands: true,
    enableReadyCheck: true,
    enableOfflineQueue: true,
    maxRetriesPerRequest: 50,
  });
  
  setupRedisEventHandlers(redisClient, 'Redis');
  
  try {
    await redisClient.connect();
    
    // Set up a ping interval to keep the connection alive if not already set
    if (!isInitialized) {
      setupPingInterval();
      isInitialized = true;
    }
    
  } catch (error) {
    console.error("Failed to connect to Redis:", error);
    redisClient = null;
    throw error;
  }
  
  return redisClient;
}

// Get or create Redis publisher client
export async function getRedisPublisherClient() {
  if (redisPublisherClient && await isRedisConnected(redisPublisherClient)) {
    console.log("Using existing Redis publisher client");
    return redisPublisherClient;
  }
  
  console.log("Creating new Redis publisher client");
  
  const redisUrl = cleanRedisUrl(process.env.REDIS_URL) || cleanRedisUrl(process.env.KV_URL);
  if (!redisUrl) {
    throw new Error("REDIS_URL or KV_URL environment variable is not set");
  }
  
  // Create Redis publisher client with maximum persistence settings
  redisPublisherClient = createClient({
    url: redisUrl,
    socket: {
      reconnectStrategy: (retries) => {
        const delay = Math.min(Math.pow(1.5, retries) * 100, 5000);
        console.log(`Redis publisher reconnecting in ${delay}ms (attempt ${retries})`);
        return delay;
      },
      connectTimeout: 30000,
      keepAlive: 5000,
      noDelay: true,
      tls: redisUrl.startsWith('rediss://') ? { rejectUnauthorized: false } : undefined,
    },
    pingInterval: 1000,
    disableOfflineQueue: false,
    commandTimeout: 5000,
    retryStrategy: () => 1000, // Retry every second
    autoResubscribe: true,
    autoResendUnfulfilledCommands: true,
    enableReadyCheck: true,
    enableOfflineQueue: true,
    maxRetriesPerRequest: 50,
  });
  
  setupRedisEventHandlers(redisPublisherClient, 'Redis Publisher');
  
  try {
    await redisPublisherClient.connect();
  } catch (error) {
    console.error("Failed to connect to Redis publisher:", error);
    redisPublisherClient = null;
    throw error;
  }
  
  return redisPublisherClient;
}

// Set up Redis event handlers
function setupRedisEventHandlers(client: any, clientName: string) {
  client.on("error", (err: Error) => {
    console.error(`${clientName} error:`, err);
  });
  
  client.on("connect", () => {
    console.log(`${clientName} connected`);
  });
  
  client.on("ready", () => {
    console.log(`${clientName} ready`);
  });
  
  client.on("reconnecting", () => {
    console.log(`${clientName} reconnecting...`);
  });
  
  client.on("end", () => {
    console.log(`${clientName} disconnected`);
    if (clientName === 'Redis') {
      redisClient = null;
    } else {
      redisPublisherClient = null;
    }
  });
}

// Check if Redis is connected
async function isRedisConnected(client: any) {
  if (!client) return false;
  
  try {
    await client.ping();
    return true;
  } catch (error) {
    console.error("Redis connection check failed:", error);
    return false;
  }
}

// Set up a ping interval to keep the connection alive
function setupPingInterval() {
  if (pingIntervalId) {
    clearInterval(pingIntervalId);
  }
  
  pingIntervalId = setInterval(async () => {
    try {
      // Ping the main Redis client
      if (redisClient) {
        await redisClient.ping();
        console.log("Redis ping successful");
      }
      
      // Ping the publisher client if it exists
      if (redisPublisherClient) {
        await redisPublisherClient.ping();
        console.log("Redis publisher ping successful");
      }
      
      // If either client is null, try to reconnect
      if (!redisClient) {
        console.log("Redis client is null, attempting to reconnect");
        try {
          await getRedisClient();
        } catch (error) {
          console.error("Failed to reconnect Redis client:", error);
        }
      }
      
      if (!redisPublisherClient) {
        console.log("Redis publisher client is null, attempting to reconnect");
        try {
          await getRedisPublisherClient();
        } catch (error) {
          console.error("Failed to reconnect Redis publisher client:", error);
        }
      }
      
    } catch (error) {
      console.error("Ping interval error:", error);
      
      // Try to reconnect if ping fails
      try {
        if (redisClient) {
          await redisClient.disconnect();
          redisClient = null;
        }
        if (redisPublisherClient) {
          await redisPublisherClient.disconnect();
          redisPublisherClient = null;
        }
        
        await getRedisClient();
        await getRedisPublisherClient();
      } catch (reconnectError) {
        console.error("Failed to reconnect after ping failure:", reconnectError);
      }
    }
  }, 10000); // Ping every 10 seconds
  
  // Ensure the interval is cleaned up on process exit
  process.on('beforeExit', () => {
    if (pingIntervalId) {
      clearInterval(pingIntervalId);
    }
  });
}

// Initialize the Redis connection immediately
getRedisClient().catch(error => {
  console.error("Initial Redis connection failed:", error);
});

// Export a function to ensure Redis connection
export async function ensureRedisConnection() {
  try {
    await getRedisClient();
    await getRedisPublisherClient();
    return true;
  } catch (error) {
    console.error("Failed to ensure Redis connection:", error);
    return false;
  }
} 