/**
 * Advanced monitoring routes for Portal Monitoring (Phase 0)
 * Provides structured logs, dependency details, and extended metrics
 * 
 * SECURITY: All monitoring endpoints require authentication in production.
 * For local development, consider network binding (127.0.0.1 only).
 */

import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { getQueue } from '../queue/index.js';
import { getRedisClient } from '../db/redis.js';
import { getPrismaClient } from '../db/prisma.js';
import { performHealthChecks } from '../health/index.js';
import { createLogger } from '@4runr/shared';
import { wrapLoggerForMonitoring } from '../middleware/log-capture.js';
import {
  getDependencyPoolsPayload,
  getMonitoringHttpPayload,
  getMonitoringRunsPayload,
  getMonitoringSsePayload,
} from '../metrics/monitoring-detail.js';

interface LogEntry {
  timestamp: string;
  level: 'error' | 'warn' | 'info' | 'debug';
  message: string;
  context?: string;
  correlationId?: string;
  data?: Record<string, unknown>;
}

// In-memory ring buffer for logs (last 1000 entries)
const LOG_BUFFER_SIZE = 1000;
const logBuffer: LogEntry[] = [];

/**
 * Add log entry to buffer (called by logger middleware or manually)
 */
export function addMonitoringLog(entry: LogEntry): void {
  logBuffer.push(entry);
  if (logBuffer.length > LOG_BUFFER_SIZE) {
    logBuffer.shift();
  }
}

/**
 * Register monitoring routes
 * @param fastify Fastify instance
 * @param options Optional configuration for route security
 */
export function registerMonitoringRoutes(
  fastify: FastifyInstance,
  options?: {
    requireAuth?: boolean | any; // boolean false or auth handler function
    readRateLimit?: any;
  }
): void {
  const { requireAuth, readRateLimit } = options || {};
  const logger = wrapLoggerForMonitoring(createLogger('Gateway:Monitoring'));

  // Determine if auth is required (requireAuth can be false, function, or undefined)
  const authHandler = requireAuth === false ? null : requireAuth;
  const preHandler = authHandler && readRateLimit
    ? [authHandler, readRateLimit]
    : authHandler
    ? [authHandler]
    : [];

  /**
   * GET /api/monitoring/logs
   * Returns last N Gateway logs for TUI display
   * SECURITY: Requires authentication (contains operational data)
   */
  fastify.get('/api/monitoring/logs', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (request: FastifyRequest, reply: FastifyReply) => {
    const query = request.query as { limit?: string; level?: string };
    const limit = Math.min(parseInt(query.limit || '100', 10), LOG_BUFFER_SIZE);
    const levelFilter = query.level as LogEntry['level'] | undefined;

    let logs = logBuffer.slice(-limit);
    
    if (levelFilter) {
      logs = logs.filter(log => log.level === levelFilter);
    }

    return {
      logs,
      count: logs.length,
      bufferSize: logBuffer.length,
      timestamp: new Date().toISOString(),
    };
  });

  /**
   * GET /api/monitoring/dependencies/detail
   * Extended dependency info (connection pools, queue stats, etc.)
   * SECURITY: Requires authentication (exposes infrastructure details)
   */
  fastify.get('/api/monitoring/dependencies/detail', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (request: FastifyRequest, reply: FastifyReply) => {
    const health = await performHealthChecks();
    const details: {
      database?: {
        status: string;
        latency?: number;
        poolAvailable?: boolean;
        pool?: { active: number; idle: number };
        telemetry?: { source: string; bestEffort: boolean; note: string };
      };
      redis?: {
        status: string;
        latency?: number;
        connectionCountAvailable?: boolean;
        connections?: { active: number };
        telemetry?: { source: string; bestEffort: boolean; note: string };
      };
      queue?: {
        status: string;
        jobs?: {
          waiting: number;
          active: number;
          completed: number;
          failed: number;
          delayed: number;
        };
      };
      shield?: {
        enabled: boolean;
        mode: string;
        detectors: {
          pii: boolean;
          injection: boolean;
          hallucination: boolean;
        };
        status: 'operational' | 'misconfigured';
        warnings?: string[];
      };
    } = {};

    const pools = await getDependencyPoolsPayload();

    // Database details
    if (health.checks.database) {
      const dbLatency = health.checks.database.responseTime;
      details.database = {
        status: health.checks.database.status,
        ...(dbLatency !== undefined && { latency: dbLatency }),
        poolAvailable: true,
        pool: {
          active: pools.database.active,
          idle: pools.database.idle,
        },
        telemetry: pools.telemetry,
      };
    }

    // Redis details
    if (health.checks.redis) {
      const redisLatency = health.checks.redis.responseTime;
      details.redis = {
        status: health.checks.redis.status,
        ...(redisLatency !== undefined && { latency: redisLatency }),
        connectionCountAvailable: true,
        connections: {
          active: pools.redis.active,
        },
        telemetry: pools.telemetry,
      };
    }

    // Queue details (real data from BullMQ)
    if (health.checks.queue) {
      details.queue = {
        status: health.checks.queue.status,
      };

      try {
        const queue = getQueue();
        if (queue) {
          // BullMQ v4+ getJobCounts: pass state names as separate args
          // Returns object with keys matching exact state names passed
          const counts = await queue.getJobCounts(
            'waiting',
            'active',
            'completed',
            'failed',
            'delayed'
          ) as Record<string, number>;

          details.queue.jobs = {
            waiting: counts['waiting'] ?? 0,
            active: counts['active'] ?? 0,
            completed: counts['completed'] ?? 0,
            failed: counts['failed'] ?? 0,
            delayed: counts['delayed'] ?? 0,
          };
        }
      } catch (err) {
        logger.error('Failed to fetch queue job counts', {
          error: err instanceof Error ? err.message : String(err),
        });
      }
    }

    // S5 fix: include Shield health (was wasted work to fetch but not return)
    if (health.checks.shield) {
      details.shield = health.checks.shield;
    }

    return {
      details,
      telemetry: pools.telemetry,
      timestamp: new Date().toISOString(),
    };
  });

  /**
   * GET /api/monitoring/metrics/http
   * Detailed HTTP metrics with route breakdown
   * SECURITY: Requires authentication
   */
  fastify.get('/api/monitoring/metrics/http', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (_request: FastifyRequest, _reply: FastifyReply) => {
    return await getMonitoringHttpPayload();
  });

  /**
   * GET /api/monitoring/metrics/runs
   * Detailed run metrics
   * SECURITY: Requires authentication
   */
  fastify.get('/api/monitoring/metrics/runs', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (_request: FastifyRequest, _reply: FastifyReply) => {
    return await getMonitoringRunsPayload();
  });

  /**
   * GET /api/monitoring/metrics/sse
   * SSE connection / message counters (Phase 3)
   */
  fastify.get('/api/monitoring/metrics/sse', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (_request: FastifyRequest, _reply: FastifyReply) => {
    return await getMonitoringSsePayload();
  });

  /**
   * GET /api/monitoring/metrics/queue
   * Detailed queue metrics with job states
   * SECURITY: Requires authentication
   */
  fastify.get('/api/monitoring/metrics/queue', {
    ...(preHandler.length > 0 && { preHandler }),
  }, async (request: FastifyRequest, reply: FastifyReply) => {
    try {
      const queue = getQueue();
      if (!queue) {
        return {
          available: false,
          message: 'Queue not initialized (Redis required)',
          timestamp: new Date().toISOString(),
        };
      }

      // BullMQ v4+ getJobCounts: returns exact keys matching state names passed
      const counts = await queue.getJobCounts(
        'waiting',
        'active',
        'completed',
        'failed',
        'delayed',
        'paused'
      ) as Record<string, number>;

      return {
        available: true,
        jobs: {
          waiting: counts['waiting'] ?? 0,
          active: counts['active'] ?? 0,
          completed: counts['completed'] ?? 0,
          failed: counts['failed'] ?? 0,
          delayed: counts['delayed'] ?? 0,
          paused: counts['paused'] ?? 0,
        },
        queueName: (queue as any).name || 'run-execution',
        timestamp: new Date().toISOString(),
      };
    } catch (error) {
      logger.error('Failed to get queue metrics', {
        error: error instanceof Error ? error.message : String(error),
      });
      return {
        available: false,
        error: error instanceof Error ? error.message : String(error),
        timestamp: new Date().toISOString(),
      };
    }
  });

  logger.info('Monitoring routes registered (Phase 0)', {
    authRequired: preHandler.length > 0,
    endpoints: [
      '/api/monitoring/logs',
      '/api/monitoring/dependencies/detail',
      '/api/monitoring/metrics/http',
      '/api/monitoring/metrics/runs',
      '/api/monitoring/metrics/sse',
      '/api/monitoring/metrics/queue',
    ],
  });
}
