/**
 * Shield Metrics Tests (S4)
 * Verifies that Shield decisions emit Prometheus metrics and Redis events
 */

import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
import {
  shieldDecisionsTotal,
  shieldBlocksTotal,
  shieldMasksTotal,
  shieldRewritesTotal,
  resetMetrics,
} from '../metrics/index.js';

describe('Shield Metrics (S4)', () => {
  beforeEach(() => {
    resetMetrics();
  });

  afterEach(() => {
    resetMetrics();
  });

  describe('shieldDecisionsTotal', () => {
    it('should increment for input decisions', async () => {
      shieldDecisionsTotal.inc({ stage: 'input', action: 'block', mode: 'enforce' });
      
      const metrics = await shieldDecisionsTotal.get();
      const blockMetric = metrics.values.find(
        (v) => v.labels.stage === 'input' && v.labels.action === 'block' && v.labels.mode === 'enforce'
      );
      
      expect(blockMetric).toBeDefined();
      expect(blockMetric?.value).toBe(1);
    });

    it('should increment for output decisions', async () => {
      shieldDecisionsTotal.inc({ stage: 'output', action: 'mask', mode: 'enforce' });
      
      const metrics = await shieldDecisionsTotal.get();
      const maskMetric = metrics.values.find(
        (v) => v.labels.stage === 'output' && v.labels.action === 'mask'
      );
      
      expect(maskMetric).toBeDefined();
      expect(maskMetric?.value).toBe(1);
    });

    it('should track monitor mode separately', async () => {
      shieldDecisionsTotal.inc({ stage: 'input', action: 'flag', mode: 'monitor' });
      
      const metrics = await shieldDecisionsTotal.get();
      const flagMetric = metrics.values.find(
        (v) => v.labels.mode === 'monitor' && v.labels.action === 'flag'
      );
      
      expect(flagMetric).toBeDefined();
    });
  });

  describe('shieldBlocksTotal', () => {
    it('should increment for injection blocks', async () => {
      shieldBlocksTotal.inc({ stage: 'input', reason: 'injection' });
      
      const metrics = await shieldBlocksTotal.get();
      const blockMetric = metrics.values.find(
        (v) => v.labels.reason === 'injection'
      );
      
      expect(blockMetric).toBeDefined();
      expect(blockMetric?.value).toBe(1);
    });

    it('should increment for hallucination blocks', async () => {
      shieldBlocksTotal.inc({ stage: 'output', reason: 'hallucination' });
      
      const metrics = await shieldBlocksTotal.get();
      const blockMetric = metrics.values.find(
        (v) => v.labels.reason === 'hallucination'
      );
      
      expect(blockMetric).toBeDefined();
    });

    it('should track stage separately', async () => {
      shieldBlocksTotal.inc({ stage: 'input', reason: 'injection' });
      shieldBlocksTotal.inc({ stage: 'output', reason: 'hallucination' });
      
      const metrics = await shieldBlocksTotal.get();
      const inputBlocks = metrics.values.find((v) => v.labels.stage === 'input');
      const outputBlocks = metrics.values.find((v) => v.labels.stage === 'output');
      
      expect(inputBlocks?.value).toBe(1);
      expect(outputBlocks?.value).toBe(1);
    });
  });

  describe('shieldMasksTotal', () => {
    it('should increment for PII masks', async () => {
      shieldMasksTotal.inc({ stage: 'output', type: 'pii' });
      
      const metrics = await shieldMasksTotal.get();
      const maskMetric = metrics.values.find((v) => v.labels.type === 'pii');
      
      expect(maskMetric).toBeDefined();
      expect(maskMetric?.value).toBe(1);
    });

    it('should increment for injection masks', async () => {
      shieldMasksTotal.inc({ stage: 'input', type: 'injection' });
      
      const metrics = await shieldMasksTotal.get();
      const maskMetric = metrics.values.find((v) => v.labels.type === 'injection');
      
      expect(maskMetric).toBeDefined();
    });
  });

  describe('shieldRewritesTotal', () => {
    it('should increment for successful rewrites', async () => {
      shieldRewritesTotal.inc({ success: 'true' });
      
      const metrics = await shieldRewritesTotal.get();
      const rewriteMetric = metrics.values.find((v) => v.labels.success === 'true');
      
      expect(rewriteMetric).toBeDefined();
      expect(rewriteMetric?.value).toBe(1);
    });

    it('should increment for failed rewrites', async () => {
      shieldRewritesTotal.inc({ success: 'false' });
      
      const metrics = await shieldRewritesTotal.get();
      const rewriteMetric = metrics.values.find((v) => v.labels.success === 'false');
      
      expect(rewriteMetric).toBeDefined();
    });

    it('should track success vs failure separately', async () => {
      shieldRewritesTotal.inc({ success: 'true' });
      shieldRewritesTotal.inc({ success: 'true' });
      shieldRewritesTotal.inc({ success: 'false' });
      
      const metrics = await shieldRewritesTotal.get();
      const successMetric = metrics.values.find((v) => v.labels.success === 'true');
      const failureMetric = metrics.values.find((v) => v.labels.success === 'false');
      
      expect(successMetric?.value).toBe(2);
      expect(failureMetric?.value).toBe(1);
    });
  });

  describe('Metrics Format', () => {
    it('should export in Prometheus format', async () => {
      shieldDecisionsTotal.inc({ stage: 'input', action: 'block', mode: 'enforce' });
      shieldBlocksTotal.inc({ stage: 'input', reason: 'injection' });
      
      const metrics = await shieldDecisionsTotal.get();
      expect(metrics).toHaveProperty('name', 'shield_decisions_total');
      expect(metrics).toHaveProperty('type', 'counter');
      expect(metrics).toHaveProperty('help');
      expect(metrics).toHaveProperty('values');
    });
  });
});
