/**
 * Prometheus metrics collection
 * Full metrics integration for observability
 */

import { Registry, Counter, Histogram, Gauge } from 'prom-client';

// Create a custom registry
const register = new Registry();

// HTTP Request Metrics
export const httpRequestDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
  registers: [register],
});

export const httpRequestTotal = new Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register],
});

export const httpRequestErrors = new Counter({
  name: 'http_request_errors_total',
  help: 'Total number of HTTP request errors',
  labelNames: ['method', 'route', 'error_type'],
  registers: [register],
});

// Run Metrics
export const runsCreated = new Counter({
  name: 'runs_created_total',
  help: 'Total number of runs created',
  labelNames: ['status'],
  registers: [register],
});

export const runsStarted = new Counter({
  name: 'runs_started_total',
  help: 'Total number of runs started',
  registers: [register],
});

export const runsCompleted = new Counter({
  name: 'runs_completed_total',
  help: 'Total number of runs completed',
  labelNames: ['status'],
  registers: [register],
});

export const runsDuration = new Histogram({
  name: 'run_duration_seconds',
  help: 'Duration of runs in seconds',
  labelNames: ['status'],
  buckets: [1, 5, 10, 30, 60, 120, 300, 600, 1800, 3600],
  registers: [register],
});

export const runsActive = new Gauge({
  name: 'runs_active',
  help: 'Number of currently active runs',
  registers: [register],
});

// SSE Metrics
export const sseConnectionsOpened = new Counter({
  name: 'sse_connections_opened_total',
  help: 'Total number of SSE connections opened',
  registers: [register],
});

export const sseConnectionsClosed = new Counter({
  name: 'sse_connections_closed_total',
  help: 'Total number of SSE connections closed',
  registers: [register],
});

export const sseActiveConnections = new Gauge({
  name: 'sse_active_connections',
  help: 'Number of currently active SSE connections',
  registers: [register],
});

export const sseMessagesTotal = new Counter({
  name: 'sse_messages_total',
  help: 'Total number of SSE messages sent',
  registers: [register],
});

// Idempotency Metrics
export const idempotencyHits = new Counter({
  name: 'idempotency_hits_total',
  help: 'Total number of idempotency cache hits',
  registers: [register],
});

export const idempotencyMisses = new Counter({
  name: 'idempotency_misses_total',
  help: 'Total number of idempotency cache misses',
  registers: [register],
});

export const idempotencyConflicts = new Counter({
  name: 'idempotency_conflicts_total',
  help: 'Total number of idempotency conflicts (same key, different body)',
  registers: [register],
});

export const idempotencyStoreSize = new Gauge({
  name: 'idempotency_store_size',
  help: 'Current number of idempotency keys in store',
  registers: [register],
});

// Rate Limiting Metrics
export const rateLimitHits = new Counter({
  name: 'rate_limit_hits_total',
  help: 'Total number of rate limit hits',
  labelNames: ['identity_type', 'endpoint'],
  registers: [register],
});

// Authentication Metrics
export const authSuccess = new Counter({
  name: 'auth_success_total',
  help: 'Total number of successful authentications',
  labelNames: ['auth_type'],
  registers: [register],
});

export const authFailures = new Counter({
  name: 'auth_failures_total',
  help: 'Total number of authentication failures',
  labelNames: ['auth_type', 'reason'],
  registers: [register],
});

// Queue Metrics
export const queueJobsQueued = new Counter({
  name: 'queue_jobs_queued_total',
  help: 'Total number of jobs queued',
  labelNames: ['queue', 'priority'],
  registers: [register],
});

export const queueJobsCompleted = new Counter({
  name: 'queue_jobs_completed_total',
  help: 'Total number of jobs completed',
  labelNames: ['queue'],
  registers: [register],
});

export const queueJobsFailed = new Counter({
  name: 'queue_jobs_failed_total',
  help: 'Total number of jobs failed',
  labelNames: ['queue'],
  registers: [register],
});

export const queueJobDuration = new Histogram({
  name: 'queue_job_duration_seconds',
  help: 'Duration of queue job processing in seconds',
  labelNames: ['queue'],
  buckets: [0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300],
  registers: [register],
});

export const queueJobsWaiting = new Gauge({
  name: 'queue_jobs_waiting',
  help: 'Number of jobs waiting in queue',
  labelNames: ['queue'],
  registers: [register],
});

export const queueJobsActive = new Gauge({
  name: 'queue_jobs_active',
  help: 'Number of jobs currently being processed',
  labelNames: ['queue'],
  registers: [register],
});

// Database Metrics
export const dbQueryDuration = new Histogram({
  name: 'db_query_duration_seconds',
  help: 'Duration of database queries in seconds',
  labelNames: ['operation', 'table'],
  buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5],
  registers: [register],
});

export const dbQueryErrors = new Counter({
  name: 'db_query_errors_total',
  help: 'Total number of database query errors',
  labelNames: ['operation', 'table', 'error_type'],
  registers: [register],
});

// Health Check Metrics
export const healthCheckTotal = new Counter({
  name: 'health_check_total',
  help: 'Total number of health check requests',
  labelNames: ['endpoint', 'status'],
  registers: [register],
});

// Uptime
export const processStartTime = new Gauge({
  name: 'process_start_time_seconds',
  help: 'Start time of the process since unix epoch in seconds',
  registers: [register],
});

// Set process start time
processStartTime.set(Date.now() / 1000);

// Connection Pool Metrics
export const dbConnectionsActive = new Gauge({
  name: 'db_connections_active',
  help: 'Number of active database connections',
  registers: [register],
});

export const dbConnectionsIdle = new Gauge({
  name: 'db_connections_idle',
  help: 'Number of idle database connections in pool',
  registers: [register],
});

// Redis Metrics
export const redisConnectionsActive = new Gauge({
  name: 'redis_connections_active',
  help: 'Number of active Redis connections',
  registers: [register],
});

export const redisCommandDuration = new Histogram({
  name: 'redis_command_duration_seconds',
  help: 'Duration of Redis commands in seconds',
  labelNames: ['command'],
  buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1],
  registers: [register],
});

// Shield Metrics (S4)
export const shieldDecisionsTotal = new Counter({
  name: 'shield_decisions_total',
  help: 'Total number of Shield decisions',
  labelNames: ['stage', 'action', 'mode'],
  registers: [register],
});

export const shieldBlocksTotal = new Counter({
  name: 'shield_blocks_total',
  help: 'Total number of Shield blocks (input or output)',
  labelNames: ['stage', 'reason'],
  registers: [register],
});

export const shieldMasksTotal = new Counter({
  name: 'shield_masks_total',
  help: 'Total number of Shield masks (PII redaction)',
  labelNames: ['stage', 'type'],
  registers: [register],
});

export const shieldRewritesTotal = new Counter({
  name: 'shield_rewrites_total',
  help: 'Total number of Shield rewrites',
  labelNames: ['success'],
  registers: [register],
});

// Sentinel Metrics (N3)
export const sentinelPolicyViolationsTotal = new Counter({
  name: 'sentinel_policy_violations_total',
  help: 'Total Sentinel policy violations observed on run path',
  labelNames: ['policy'],
  registers: [register],
});

export const sentinelKillsTotal = new Counter({
  name: 'sentinel_kills_total',
  help: 'Total Sentinel run kills persisted to store',
  labelNames: ['trigger'],
  registers: [register],
});

// Memory Metrics
export const memoryUsage = new Gauge({
  name: 'process_memory_usage_bytes',
  help: 'Process memory usage in bytes',
  labelNames: ['type'],
  registers: [register],
});

// CPU Metrics
export const cpuUsage = new Gauge({
  name: 'process_cpu_usage_seconds',
  help: 'Process CPU usage in seconds',
  labelNames: ['type'],
  registers: [register],
});

// Export the registry for metrics endpoint
export { register };

/**
 * Get metrics in Prometheus format
 */
export async function getMetrics(): Promise<string> {
  // Update memory metrics before export
  const mem = process.memoryUsage();
  memoryUsage.set({ type: 'rss' }, mem.rss);
  memoryUsage.set({ type: 'heapTotal' }, mem.heapTotal);
  memoryUsage.set({ type: 'heapUsed' }, mem.heapUsed);
  memoryUsage.set({ type: 'external' }, mem.external);
  
  // Update CPU metrics
  const cpuUsageData = process.cpuUsage();
  cpuUsage.set({ type: 'user' }, cpuUsageData.user / 1000000); // Convert to seconds
  cpuUsage.set({ type: 'system' }, cpuUsageData.system / 1000000);
  
  return await register.metrics();
}

/**
 * Get metrics summary for monitoring dashboard
 */
export async function getMetricsSummary(): Promise<any> {
  const metrics = await getMetrics();
  const mem = process.memoryUsage();
  const cpuData = process.cpuUsage();
  const uptime = process.uptime();
  
  return {
    uptime: {
      seconds: uptime,
      formatted: formatUptime(uptime)
    },
    memory: {
      rss: mem.rss,
      heapTotal: mem.heapTotal,
      heapUsed: mem.heapUsed,
      heapUsedPercent: ((mem.heapUsed / mem.heapTotal) * 100).toFixed(1),
      external: mem.external
    },
    cpu: {
      user: (cpuData.user / 1000000).toFixed(2), // seconds
      system: (cpuData.system / 1000000).toFixed(2)
    },
    timestamp: new Date().toISOString()
  };
}

/**
 * Format uptime in human-readable format
 */
function formatUptime(seconds: number): string {
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);
  
  const parts: string[] = [];
  if (days > 0) parts.push(`${days}d`);
  if (hours > 0) parts.push(`${hours}h`);
  if (minutes > 0) parts.push(`${minutes}m`);
  if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
  
  return parts.join(' ');
}

/**
 * Reset all metrics (useful for testing)
 */
export function resetMetrics(): void {
  register.resetMetrics();
}

