/**
 * Health check utilities
 * Provides comprehensive health checking for dependencies and system status
 */

import { getRedisClient, isRedisAvailable } from '../db/redis.js';
import { getQueue } from '../queue/index.js';
import { getPrismaClient } from '../db/prisma.js';
import { Shield } from '@4runr/shield';
import { getShieldMisconfigurationWarnings } from '../security/shield-config.js';
import { env, createLogger } from '@4runr/shared';

const logger = createLogger('Gateway:Health');

function databaseUrlConfigured(): boolean {
  return Boolean(env.DATABASE_URL || process.env['DATABASE_URL']);
}

function redisUrlConfigured(): boolean {
  return Boolean(env.REDIS_URL || process.env['REDIS_URL']);
}

/** Best-effort stringify for Promise rejection reasons (Error, string, primitives, etc.). */
function rejectionMessage(reason: unknown, fallback: string): string {
  if (reason instanceof Error) return reason.message;
  if (typeof reason === 'string') return reason;
  if (reason === undefined || reason === null) return fallback;
  return String(reason);
}

export interface HealthCheckResult {
  status: 'healthy' | 'unhealthy' | 'degraded';
  checks: {
    database?: DependencyCheck;
    redis?: DependencyCheck;
    queue?: DependencyCheck;
    shield?: ShieldCheck;
  };
  timestamp: string;
}

export interface DependencyCheck {
  status: 'up' | 'down' | 'degraded';
  responseTime?: number;
  error?: string;
  message?: string;
}

export interface ShieldCheck {
  enabled: boolean;
  mode: string;
  detectors: {
    pii: boolean;
    injection: boolean;
    hallucination: boolean;
  };
  status: 'operational' | 'misconfigured';
  warnings?: string[];
}

/**
 * Check database connectivity
 */
export async function checkDatabase(): Promise<DependencyCheck> {
  const startTime = Date.now();

  if (!databaseUrlConfigured()) {
    return {
      status: 'up',
      responseTime: Date.now() - startTime,
      message: 'DATABASE_URL not set — no Postgres configured for this process (skipping DB probe)',
    };
  }

  try {
    const prisma = await getPrismaClient();
    await prisma.$queryRaw`SELECT 1`;
    const responseTime = Date.now() - startTime;

    return {
      status: 'up',
      responseTime,
      message: 'Postgres reachable (SELECT 1)',
    };
  } catch (error) {
    const responseTime = Date.now() - startTime;
    logger.warn('Database health check failed', {
      error: error instanceof Error ? error.message : String(error),
    });

    return {
      status: 'down',
      responseTime,
      error: error instanceof Error ? error.message : String(error),
      message: 'Postgres not reachable or Prisma client failed',
    };
  }
}

/**
 * Check Redis connectivity
 */
export async function checkRedis(): Promise<DependencyCheck> {
  const startTime = Date.now();
  
  try {
    if (!redisUrlConfigured()) {
      return {
        status: 'up',
        responseTime: Date.now() - startTime,
        message: 'REDIS_URL not set — Redis not required for readiness',
      };
    }

    const available = await isRedisAvailable();
    if (!available) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        message: 'REDIS_URL set but Redis is unreachable (start redis-server or Docker, then retry)',
      };
    }

    const redis = await getRedisClient();
    if (!redis) {
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        message: 'Redis client not available',
      };
    }

    // Simple ping test
    const result = await redis.ping();
    const responseTime = Date.now() - startTime;

    if (result === 'PONG') {
      return {
        status: 'up',
        responseTime,
        message: 'Redis connection healthy',
      };
    }

    return {
      status: 'degraded',
      responseTime,
      message: 'Redis ping returned unexpected result',
    };
  } catch (error) {
    const responseTime = Date.now() - startTime;
    logger.warn('Redis health check failed', {
      error: error instanceof Error ? error.message : String(error),
    });

    return {
      status: 'down',
      responseTime,
      error: error instanceof Error ? error.message : String(error),
      message: 'Redis connection failed',
    };
  }
}

/**
 * Check queue system status
 */
export async function checkQueue(): Promise<DependencyCheck> {
  const startTime = Date.now();

  try {
    const queue = getQueue();
    if (!queue) {
      if (!redisUrlConfigured()) {
        return {
          status: 'up',
          responseTime: Date.now() - startTime,
          message: 'No run queue (Redis not configured)',
        };
      }
      return {
        status: 'down',
        responseTime: Date.now() - startTime,
        message: 'Run queue not initialized — Redis configured but BullMQ queue unavailable',
      };
    }

    const queueName = (queue as { name?: string }).name ?? 'unknown';
    const responseTime = Date.now() - startTime;

    return {
      status: 'up',
      responseTime,
      message: `Run queue ready (${queueName})`,
    };
  } catch (error) {
    const responseTime = Date.now() - startTime;
    logger.warn('Queue health check failed', {
      error: error instanceof Error ? error.message : String(error),
    });

    return {
      status: 'down',
      responseTime,
      error: error instanceof Error ? error.message : String(error),
      message: 'Queue system check failed',
    };
  }
}

/**
 * Check Shield AI safety layer status (S5)
 */
export async function checkShield(): Promise<ShieldCheck> {
  try {
    const shield = Shield.getInstance();
    const health = shield.getHealth();
    const config = shield.getConfig();
    
    // S5 fix: use shared helper and same API key source as startup
    const openAiApiKey = process.env['OPENAI_API_KEY'];
    const warnings = getShieldMisconfigurationWarnings(config, openAiApiKey);

    const status: ShieldCheck['status'] = warnings.length > 0 ? 'misconfigured' : 'operational';

    return {
      enabled: shield.isEnabled(), // Use isEnabled() for effective state
      mode: health.mode,
      detectors: health.detectors,
      status,
      ...(warnings.length > 0 && { warnings }),
    };
  } catch (error) {
    logger.warn('Shield health check failed', {
      error: error instanceof Error ? error.message : String(error),
    });

    return {
      enabled: false,
      mode: 'unknown',
      detectors: {
        pii: false,
        injection: false,
        hallucination: false,
      },
      status: 'misconfigured',
      warnings: ['Shield health check threw error'],
    };
  }
}

/**
 * Perform all health checks (S5: includes Shield)
 */
export async function performHealthChecks(): Promise<HealthCheckResult> {
  const checks: HealthCheckResult['checks'] = {};

  // Run checks in parallel
  const [databaseCheck, redisCheck, queueCheck, shieldCheck] = await Promise.allSettled([
    checkDatabase(),
    checkRedis(),
    checkQueue(),
    checkShield(),
  ]);

  if (databaseCheck.status === 'fulfilled') {
    checks.database = databaseCheck.value;
  } else {
    const msg = rejectionMessage(databaseCheck.reason, 'Database check failed');
    checks.database = {
      status: 'down',
      message: msg,
      error: msg,
    };
  }

  if (redisCheck.status === 'fulfilled') {
    checks.redis = redisCheck.value;
  } else {
    const msg = rejectionMessage(redisCheck.reason, 'Redis check failed');
    checks.redis = {
      status: 'down',
      message: msg,
      error: msg,
    };
  }

  if (queueCheck.status === 'fulfilled') {
    checks.queue = queueCheck.value;
  } else {
    const msg = rejectionMessage(queueCheck.reason, 'Queue check failed');
    checks.queue = {
      status: 'down',
      message: msg,
      error: msg,
    };
  }

  if (shieldCheck.status === 'fulfilled') {
    checks.shield = shieldCheck.value;
  } else {
    const msg = rejectionMessage(shieldCheck.reason, 'Shield check failed');
    checks.shield = {
      enabled: false,
      mode: 'unknown',
      detectors: { pii: false, injection: false, hallucination: false },
      status: 'misconfigured',
      warnings: [msg],
    };
  }

  const db = checks.database;
  const redis = checks.redis;
  const queue = checks.queue;

  const dbRequired = databaseUrlConfigured();
  const redisRequired = redisUrlConfigured();

  const databaseDown = dbRequired && db?.status === 'down';
  const redisDown = redisRequired && redis?.status === 'down';
  const queueDown = redisRequired && queue?.status === 'down';

  const hasDegraded = Object.values(checks).some(
    (check) => check?.status === 'degraded'
  );

  // Queue failure alone must not mark "unhealthy": Gateway may still serve traffic and use sync
  // execution when BullMQ did not initialize; DB + Redis are the hard readiness gates.
  // Shield misconfiguration is reported but does not gate health status (S5).
  let status: HealthCheckResult['status'] = 'healthy';
  if (databaseDown || redisDown) {
    status = 'unhealthy';
  } else if (queueDown || hasDegraded) {
    status = 'degraded';
  }

  return {
    status,
    checks,
    timestamp: new Date().toISOString(),
  };
}

/**
 * Ready for traffic when configured Postgres and Redis pass probes.
 * Queue (BullMQ) is reported in checks but does not gate readiness — startup races or a failed
 * worker init would otherwise keep /ready at 503 while the process is otherwise usable.
 */
export function computeReady(health: HealthCheckResult): boolean {
  const dbRequired = databaseUrlConfigured();
  const redisRequired = redisUrlConfigured();

  if (dbRequired && health.checks.database?.status !== 'up') {
    return false;
  }
  if (redisRequired && health.checks.redis?.status !== 'up') {
    return false;
  }
  return true;
}

export async function isReady(): Promise<boolean> {
  const health = await performHealthChecks();
  return computeReady(health);
}

/**
 * Check if service is alive (simple liveness check)
 */
export function isAlive(): boolean {
  // Liveness is simple - just check if process is running
  // More complex checks could verify memory, CPU, etc.
  return true;
}

