import { Router, Request, Response, NextFunction } from 'express';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { observabilityRateLimit } from '../../middleware/rate-limit';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import { setServiceUp } from '../../utils/metrics';
import { fetchWithTimeout } from '../../utils/fetch-with-timeout';
import { validatePromQLQueries } from '../../utils/promql-validator';
import { isServiceOnline } from '../../utils/health-check';

const router = Router();
router.use(authenticate);
router.use(requireRole('SUPER_ADMIN'));
// Stricter rate limit for observability endpoints (info disclosure risk)
router.use(observabilityRateLimit);

// Removed duplicated isServiceOnline - now using shared utility from utils/health-check.ts

// GET /api/observability/status — check health of all 7 monitoring services
router.get(
  '/status',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const [
        prometheusOnline,
        grafanaOnline,
        alertmanagerOnline,
        cadvisorOnline,
        nodeExporterOnline,
        redisExporterOnline,
        gotifyOnline,
      ] = await Promise.all([
        isServiceOnline(`${env.PROMETHEUS_URL}/api/v1/status/config`),
        isServiceOnline(`${env.GRAFANA_URL}/api/health`),
        isServiceOnline(`${env.ALERTMANAGER_URL}/api/v2/status`),
        isServiceOnline(env.CADVISOR_URL),
        isServiceOnline(`${env.NODE_EXPORTER_URL}/metrics`),
        isServiceOnline(`${env.REDIS_EXPORTER_URL}/metrics`),
        isServiceOnline(`${env.GOTIFY_URL}/health`),
      ]);

      // Update Prometheus gauges
      setServiceUp('prometheus', prometheusOnline);
      setServiceUp('grafana', grafanaOnline);
      setServiceUp('alertmanager', alertmanagerOnline);
      setServiceUp('cadvisor', cadvisorOnline);
      setServiceUp('node_exporter', nodeExporterOnline);
      setServiceUp('redis_exporter', redisExporterOnline);
      setServiceUp('gotify', gotifyOnline);

      res.json({
        prometheus: { online: prometheusOnline, url: `http://localhost:${env.PROMETHEUS_PORT}` },
        grafana: { online: grafanaOnline, url: `http://localhost:${env.GRAFANA_PORT}` },
        alertmanager: { online: alertmanagerOnline, url: `http://localhost:${env.ALERTMANAGER_PORT}` },
        cadvisor: { online: cadvisorOnline, url: `http://localhost:${env.CADVISOR_PORT}` },
        nodeExporter: { online: nodeExporterOnline, url: `http://localhost:${env.NODE_EXPORTER_PORT}` },
        redisExporter: { online: redisExporterOnline, url: `http://localhost:${env.REDIS_EXPORTER_PORT}` },
        gotify: { online: gotifyOnline, url: `http://localhost:${env.GOTIFY_PORT}` },
      });
    } catch (err) {
      logger.error('Failed to check observability services status', err);
      next(err);
    }
  },
);

// GET /api/observability/metrics-summary — fetch key metrics from Prometheus
router.get(
  '/metrics-summary',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      // Query Prometheus for key metrics
      const prometheusOnline = await isServiceOnline(`${env.PROMETHEUS_URL}/api/v1/status/config`);

      if (!prometheusOnline) {
        return res.json({
          apiUptime: { value: 0, unit: 's' },
          emailQueueSize: { value: 0, unit: 'jobs' },
          requestRate: { value: 0, unit: 'req/s' },
          emailErrorRate: { value: 0, unit: '%' },
          activeSessions: { value: 0, unit: 'sessions' },
          activeCanvassSessions: { value: 0, unit: 'sessions' },
          redisHealth: { value: 0, unit: 'status' },
        });
      }

      // Define and validate PromQL queries
      const promqlQueries = [
        'time()-process_start_time_seconds',
        'cm_email_queue_size',
        'rate(http_request_total[5m])',
        'rate(cm_email_send_errors_total[5m])*100',
        'cm_active_sessions',
        'cm_active_canvass_sessions',
        'redis_up',
      ];

      // Validate all queries before executing (prevents injection)
      validatePromQLQueries(promqlQueries);

      // Fetch metrics in parallel (with 5s timeout per query)
      const queries = promqlQueries.map(query =>
        fetchWithTimeout(`${env.PROMETHEUS_URL}/api/v1/query?query=${encodeURIComponent(query)}`, {}, 5000).then(r => r.json())
      );

      const [
        uptimeRes,
        queueSizeRes,
        requestRateRes,
        errorRateRes,
        sessionsRes,
        canvassSessionsRes,
        redisHealthRes,
      ] = (await Promise.all(queries)) as any[];

      res.json({
        apiUptime: {
          value: parseFloat(uptimeRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 's',
        },
        emailQueueSize: {
          value: parseFloat(queueSizeRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 'jobs',
        },
        requestRate: {
          value: parseFloat(requestRateRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 'req/s',
        },
        emailErrorRate: {
          value: parseFloat(errorRateRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: '%',
        },
        activeSessions: {
          value: parseFloat(sessionsRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 'sessions',
        },
        activeCanvassSessions: {
          value: parseFloat(canvassSessionsRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 'sessions',
        },
        redisHealth: {
          value: parseFloat(redisHealthRes?.data?.result?.[0]?.value?.[1] || '0'),
          unit: 'status',
        },
      });
    } catch (err) {
      logger.error('Failed to fetch metrics summary', err);
      // Return default values on error
      res.json({
        apiUptime: { value: 0, unit: 's' },
        emailQueueSize: { value: 0, unit: 'jobs' },
        requestRate: { value: 0, unit: 'req/s' },
        emailErrorRate: { value: 0, unit: '%' },
        activeSessions: { value: 0, unit: 'sessions' },
        activeCanvassSessions: { value: 0, unit: 'sessions' },
        redisHealth: { value: 0, unit: 'status' },
      });
    }
  },
);

// GET /api/observability/alerts — fetch active alerts from Alertmanager
router.get(
  '/alerts',
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const alertmanagerOnline = await isServiceOnline(`${env.ALERTMANAGER_URL}/api/v2/status`);

      if (!alertmanagerOnline) {
        return res.json({
          total: 0,
          critical: 0,
          warning: 0,
          info: 0,
          alerts: [],
        });
      }

      const response = await fetchWithTimeout(`${env.ALERTMANAGER_URL}/api/v2/alerts?active=true`, {}, 3000);
      const alerts = (await response.json()) as Array<any>;

      // Parse alerts by severity
      let critical = 0;
      let warning = 0;
      let info = 0;

      const parsedAlerts = (alerts || []).map((alert: any) => {
        const severity = alert.labels?.severity || 'info';
        if (severity === 'critical') critical++;
        else if (severity === 'warning') warning++;
        else info++;

        return {
          id: alert.fingerprint || Math.random().toString(),
          severity: severity as 'critical' | 'warning' | 'info',
          name: alert.labels?.alertname || 'Unknown',
          summary: alert.annotations?.summary || alert.annotations?.description || 'No description',
          description: alert.annotations?.description,
          startsAt: alert.startsAt,
        };
      });

      res.json({
        total: parsedAlerts.length,
        critical,
        warning,
        info,
        alerts: parsedAlerts,
      });
    } catch (err) {
      logger.error('Failed to fetch alerts', err);
      res.json({
        total: 0,
        critical: 0,
        warning: 0,
        info: 0,
        alerts: [],
      });
    }
  },
);

// GET /api/observability/event-bus — EventBus stats
router.get(
  '/event-bus',
  async (_req: Request, res: Response) => {
    const { eventBus } = await import('../../services/event-bus.service');
    res.json(eventBus.getStats());
  },
);

// GET /api/observability/engagement-leaderboard — Top engaged contacts
router.get(
  '/engagement-leaderboard',
  async (req: Request, res: Response) => {
    const { engagementScoring } = await import('../../services/event-listeners/engagement-scoring.listener');
    const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
    const leaderboard = await engagementScoring.getLeaderboard(limit);
    res.json({ leaderboard, count: leaderboard.length });
  },
);

export const observabilityRouter = router;
