/**
 * Startup Warmup Module
 * Pre-warms database and cache connections to reduce cold-start latency
 *
 * This module provides functions to:
 * 1. Warm Prisma connection pool by running a simple query
 * 2. Establish Redis connection with a PING
 * 3. Measure and report startup metrics for monitoring
 */

import { prisma } from './db';
import { redis, redisConfigured } from './redis';
import { logger } from './logger';

// Scoped logger for warmup operations
const warmupLogger = logger.scope('Warmup');

/**
 * Metrics returned after warming up connections
 */
export interface WarmupMetrics {
  /** Time taken to warm Prisma connection pool (ms) */
  prismaWarmupMs: number;
  /** Time taken to establish Redis connection (ms) */
  redisWarmupMs: number;
  /** Total warmup time (ms) */
  totalWarmupMs: number;
  /** ISO timestamp when warmup completed */
  timestamp: string;
  /** Any errors encountered during warmup */
  errors: string[];
  /** Whether warmup was successful */
  success: boolean;
}

/**
 * Performance budget thresholds for Lighthouse CI integration
 */
export interface PerformanceBudget {
  /** Time to first byte target (ms) */
  ttfb: number;
  /** 95th percentile response time target (ms) */
  p95: number;
  /** Maximum warmup time target (ms) */
  warmup: number;
}

/**
 * Startup metrics for monitoring and alerting
 */
export interface StartupMetrics {
  /** TTFB baseline measurement (ms) */
  ttfbBaseline: number;
  /** Prisma connection establishment time (ms) */
  prismaConnectionMs: number;
  /** Redis connection establishment time (ms) */
  redisConnectionMs: number;
  /** Total startup time (ms) */
  totalStartupMs: number;
  /** Whether all connections are healthy */
  healthy: boolean;
  /** Timestamp when metrics were recorded */
  timestamp: string;
  /** Performance budget compliance */
  withinBudget: boolean;
}

/**
 * Default performance budgets for Lighthouse CI
 * These values align with Core Web Vitals recommendations
 */
export const PERFORMANCE_BUDGETS: PerformanceBudget = {
  ttfb: 800,    // Target TTFB under 800ms (Good threshold)
  p95: 2000,    // Target P95 under 2s
  warmup: 3000, // Target warmup under 3s
};

/**
 * Warms the Convex-backed database connection by executing a lightweight query
 * ($queryRaw is stubbed to return [] in the Convex adapter, so we use a real
 * adapter call to verify connectivity)
 *
 * @returns Time taken in milliseconds, or -1 if failed
 */
async function warmDatabase(): Promise<{ timeMs: number; error?: string }> {
  const start = performance.now();

  try {
    // Execute a minimal findMany with limit 1 to verify Convex connectivity
    // This goes through the prisma-convex-adapter and actually hits Convex
    await prisma.organizations.findMany({ take: 1 });

    const timeMs = Math.round(performance.now() - start);
    warmupLogger.info('Database (Convex) connection warmed', { timeMs });

    return { timeMs };
  } catch (error) {
    const timeMs = Math.round(performance.now() - start);
    const errorMessage = error instanceof Error ? error.message : String(error);

    warmupLogger.error('Database warmup failed', error, { timeMs });

    return { timeMs: -1, error: errorMessage };
  }
}

/**
 * Warms Redis connection by sending a PING command
 * This ensures Redis is reachable and authenticated
 *
 * @returns Time taken in milliseconds, or -1 if failed
 */
async function warmRedis(): Promise<{ timeMs: number; error?: string }> {
  const start = performance.now();

  // Skip if Redis is not configured
  if (!redisConfigured) {
    warmupLogger.info('Redis warmup skipped (not configured)');
    return { timeMs: 0 };
  }

  try {
    const result = await redis.ping();

    const timeMs = Math.round(performance.now() - start);

    if (result === 'PONG') {
      warmupLogger.info('Redis connection warmed', { timeMs });
      return { timeMs };
    }

    warmupLogger.warn('Redis PING returned unexpected response', { result, timeMs });
    return { timeMs, error: `Unexpected PING response: ${result}` };
  } catch (error) {
    const timeMs = Math.round(performance.now() - start);
    const errorMessage = error instanceof Error ? error.message : String(error);

    warmupLogger.error('Redis warmup failed', error, { timeMs });

    return { timeMs: -1, error: errorMessage };
  }
}

/**
 * Warms all connections (Prisma and Redis) in parallel
 * This should be called during application startup
 *
 * @returns WarmupMetrics with timing and error information
 */
export async function warmupConnections(): Promise<WarmupMetrics> {
  const totalStart = performance.now();
  const errors: string[] = [];

  warmupLogger.info('Starting connection warmup');

  // Warm connections in parallel for faster startup
  const [prismaResult, redisResult] = await Promise.all([
    warmDatabase(),
    warmRedis(),
  ]);

  // Collect errors
  if (prismaResult.error) {
    errors.push(`Prisma: ${prismaResult.error}`);
  }
  if (redisResult.error) {
    errors.push(`Redis: ${redisResult.error}`);
  }

  const totalWarmupMs = Math.round(performance.now() - totalStart);
  const success = errors.length === 0 && prismaResult.timeMs >= 0;

  const metrics: WarmupMetrics = {
    prismaWarmupMs: prismaResult.timeMs,
    redisWarmupMs: redisResult.timeMs,
    totalWarmupMs,
    timestamp: new Date().toISOString(),
    errors,
    success,
  };

  if (success) {
    warmupLogger.info('Connection warmup completed successfully', {
      prismaMs: metrics.prismaWarmupMs,
      redisMs: metrics.redisWarmupMs,
      totalMs: metrics.totalWarmupMs,
    });
  } else {
    warmupLogger.warn('Connection warmup completed with errors', {
      errors,
      prismaMs: metrics.prismaWarmupMs,
      redisMs: metrics.redisWarmupMs,
      totalMs: metrics.totalWarmupMs,
    });
  }

  return metrics;
}

/**
 * Measures comprehensive startup metrics for monitoring
 * Includes TTFB baseline and connection establishment times
 *
 * @returns StartupMetrics for Lighthouse CI integration
 */
export async function measureStartupMetrics(): Promise<StartupMetrics> {
  const totalStart = performance.now();

  // Measure individual connection times
  const prismaStart = performance.now();
  let prismaHealthy = false;
  try {
    // Use a lightweight adapter call instead of $queryRaw (which is stubbed in Convex adapter)
    await prisma.organizations.findMany({ take: 1 });
    prismaHealthy = true;
  } catch {
    // Connection failed
  }
  const prismaConnectionMs = Math.round(performance.now() - prismaStart);

  const redisStart = performance.now();
  let redisHealthy = false;
  if (redisConfigured) {
    try {
      const result = await redis.ping();
      redisHealthy = result === 'PONG';
    } catch {
      // Connection failed
    }
  } else {
    // Redis not configured is acceptable in development
    redisHealthy = process.env.NODE_ENV !== 'production';
  }
  const redisConnectionMs = Math.round(performance.now() - redisStart);

  const totalStartupMs = Math.round(performance.now() - totalStart);

  // TTFB baseline is approximated as the time to first database response
  const ttfbBaseline = prismaConnectionMs;

  const healthy = prismaHealthy && redisHealthy;
  const withinBudget =
    ttfbBaseline <= PERFORMANCE_BUDGETS.ttfb &&
    totalStartupMs <= PERFORMANCE_BUDGETS.warmup;

  const metrics: StartupMetrics = {
    ttfbBaseline,
    prismaConnectionMs,
    redisConnectionMs,
    totalStartupMs,
    healthy,
    timestamp: new Date().toISOString(),
    withinBudget,
  };

  warmupLogger.info('Startup metrics recorded', {
    ...metrics,
    budgets: PERFORMANCE_BUDGETS,
  });

  return metrics;
}

/**
 * Initializes warm paths for the application
 * Call this during server startup to reduce cold-start latency
 *
 * This function:
 * 1. Warms database and cache connections
 * 2. Logs metrics for monitoring
 * 3. Does NOT throw errors - warmup failures are logged but non-blocking
 */
export async function initializeWarmPaths(): Promise<void> {
  try {
    warmupLogger.info('Initializing warm paths');

    const warmupMetrics = await warmupConnections();

    if (!warmupMetrics.success) {
      warmupLogger.warn('Warm paths initialization had issues', {
        errors: warmupMetrics.errors,
      });
    }

    // Check if within performance budget
    if (warmupMetrics.totalWarmupMs > PERFORMANCE_BUDGETS.warmup) {
      warmupLogger.warn('Warmup exceeded performance budget', {
        actual: warmupMetrics.totalWarmupMs,
        budget: PERFORMANCE_BUDGETS.warmup,
      });
    }

    warmupLogger.info('Warm paths initialized', {
      totalMs: warmupMetrics.totalWarmupMs,
      success: warmupMetrics.success,
    });
  } catch (error) {
    // Log but don't throw - warmup should not block app startup
    warmupLogger.error('Failed to initialize warm paths', error);
  }
}

/**
 * Check if the application is ready to serve requests
 * Useful for Kubernetes readiness probes
 *
 * @returns true if all critical services are available
 */
export async function isApplicationReady(): Promise<boolean> {
  try {
    // Check database (Convex) connectivity via adapter
    await prisma.organizations.findMany({ take: 1 });

    // Check Redis (if configured)
    if (redisConfigured) {
      const result = await redis.ping();
      if (result !== 'PONG') {
        return false;
      }
    }

    return true;
  } catch {
    return false;
  }
}
