import { createClient } from "redis";
import type { VercelRequest, VercelResponse } from '@vercel/node';

// Global Redis client that persists between function invocations
let redisClient: any = null;
let lastPingTime = 0;
let connectionAttempts = 0;
const MAX_CONNECTION_ATTEMPTS = 10;

// 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
async function getRedisClient() {
  if (redisClient && await isRedisConnected()) {
    console.log("Using existing Redis client");
    connectionAttempts = 0;
    return redisClient;
  }
  
  console.log("Creating new Redis client");
  connectionAttempts++;
  
  if (connectionAttempts > MAX_CONNECTION_ATTEMPTS) {
    console.log(`Exceeded maximum connection attempts (${MAX_CONNECTION_ATTEMPTS}). Resetting counter.`);
    connectionAttempts = 1;
  }
  
  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,
  });
  
  redisClient.on("error", (err: Error) => {
    console.error("Redis error", err);
    // Don't set redisClient to null here, let the reconnection strategy handle it
  });
  
  redisClient.on("connect", () => {
    console.log("Redis connected");
  });
  
  redisClient.on("ready", () => {
    console.log("Redis ready");
  });
  
  redisClient.on("end", () => {
    console.log("Redis disconnected");
    redisClient = null; // Reset client so we create a new one next time
  });
  
  try {
    await redisClient.connect();
    
    // Set up a ping interval to keep the connection alive
    setInterval(async () => {
      try {
        if (redisClient) {
          await redisClient.ping();
          console.log("Internal ping successful");
        }
      } catch (error) {
        console.error("Internal ping failed:", error);
      }
    }, 10000); // Ping every 10 seconds
    
  } catch (error) {
    console.error("Failed to connect to Redis:", error);
    redisClient = null;
    throw error;
  }
  
  return redisClient;
}

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

// Perform Redis operations to keep the connection active
async function performRedisOperations(redis: any) {
  const now = Date.now();
  
  // Store the current time in Redis
  await redis.set('last_cron_keepalive', now.toString());
  
  // Get the stored value
  const storedValue = await redis.get('last_cron_keepalive');
  
  // Get some stats
  const info = await redis.info();
  
  // Perform a simple list operation
  await redis.lPush('keepalive_list', now.toString());
  await redis.lTrim('keepalive_list', 0, 9); // Keep only the last 10 entries
  
  // Get the list
  const list = await redis.lRange('keepalive_list', 0, -1);
  
  return {
    storedValue,
    info: info.substring(0, 500) + '...', // Truncate info to avoid large response
    list
  };
}

// Handler for the cron endpoint
export default async function handler(req: VercelRequest, res: VercelResponse) {
  try {
    // Check for authorization header if needed
    const authHeader = req.headers.authorization;
    if (process.env.CRON_SECRET && authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
      return res.status(401).json({ error: 'Unauthorized' });
    }
    
    const now = Date.now();
    const timeSinceLastPing = now - lastPingTime;
    lastPingTime = now;
    
    console.log(`Cron keep-alive endpoint called. Time since last ping: ${timeSinceLastPing}ms`);
    
    const redis = await getRedisClient();
    const pingResult = await redis.ping();
    
    // Perform various Redis operations to ensure the connection stays active
    const operationResults = await performRedisOperations(redis);
    
    res.status(200).json({
      status: 'ok',
      ping: pingResult,
      lastPingTime: lastPingTime,
      timeSinceLastPing: timeSinceLastPing,
      connectionAttempts,
      operations: operationResults,
      timestamp: now
    });
  } catch (error) {
    console.error("Cron keep-alive error:", error);
    res.status(500).json({ 
      status: 'error', 
      message: error instanceof Error ? error.message : 'Unknown error',
      timestamp: Date.now()
    });
  }
} 