import { describe, it, expect } from 'vitest';
import {
  validateDatadogHmac,
  validatePagerdutyHmac,
  validateOpsgenieHmac,
  normalizeDatadog,
  normalizePagerduty,
  normalizeOpsgenie,
} from '../../src/ingestion/webhook.js';
import crypto from 'node:crypto';

const SECRET = 'test-webhook-secret';

describe('HMAC Validation', () => {
  describe('Datadog', () => {
    it('accepts valid signature with fresh timestamp', () => {
      const body = '{"alert_id":"123"}';
      const ts = Math.floor(Date.now() / 1000).toString();
      const sig = crypto.createHmac('sha256', SECRET).update(ts + body).digest('hex');

      const result = validateDatadogHmac(body, sig, ts, SECRET);
      expect(result.valid).toBe(true);
    });

    it('rejects stale timestamp (>5 minutes)', () => {
      const body = '{"alert_id":"123"}';
      const ts = (Math.floor(Date.now() / 1000) - 301).toString();
      const sig = crypto.createHmac('sha256', SECRET).update(ts + body).digest('hex');

      const result = validateDatadogHmac(body, sig, ts, SECRET);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('stale timestamp');
    });

    it('rejects missing signature', () => {
      const result = validateDatadogHmac('{}', undefined, '123', SECRET);
      expect(result.valid).toBe(false);
    });

    it('rejects invalid signature', () => {
      const ts = Math.floor(Date.now() / 1000).toString();
      const result = validateDatadogHmac('{}', 'bad-sig', ts, SECRET);
      expect(result.valid).toBe(false);
    });
  });

  describe('PagerDuty', () => {
    it('rejects missing signature', () => {
      const result = validatePagerdutyHmac('{}', undefined, SECRET);
      expect(result.valid).toBe(false);
    });

    it('rejects malformed signature', () => {
      const result = validatePagerdutyHmac('{}', 'garbage', SECRET);
      expect(result.valid).toBe(false);
    });
  });

  describe('OpsGenie', () => {
    it('rejects stale timestamp from payload body', () => {
      const staleTs = Date.now() - 6 * 60 * 1000; // 6 minutes ago
      const body = JSON.stringify({ alert: { alertId: '1' }, timestamp: staleTs });
      const sig = crypto.createHmac('sha256', SECRET).update(body).digest('hex');

      const result = validateOpsgenieHmac(body, sig, SECRET);
      expect(result.valid).toBe(false);
      expect(result.error).toContain('stale timestamp');
    });

    it('accepts fresh payload', () => {
      const body = JSON.stringify({ alert: { alertId: '1' }, timestamp: Date.now() });
      const sig = crypto.createHmac('sha256', SECRET).update(body).digest('hex');

      const result = validateOpsgenieHmac(body, sig, SECRET);
      expect(result.valid).toBe(true);
    });
  });
});

describe('Payload Normalizers', () => {
  it('normalizes Datadog payload', () => {
    const alert = normalizeDatadog({
      alert_id: 'dd-123',
      title: 'High CPU',
      priority: 'P1',
      alert_transition: 'Triggered',
      tags: { service: 'auth', env: 'prod' },
      date_happened: 1709251200,
    });
    expect(alert.sourceProvider).toBe('datadog');
    expect(alert.severity).toBe('critical');
    expect(alert.status).toBe('firing');
    expect(alert.service).toBe('auth');
  });

  it('normalizes PagerDuty payload', () => {
    const alert = normalizePagerduty({
      event: {
        event_type: 'incident.triggered',
        data: {
          id: 'pd-456',
          title: 'Disk Full',
          urgency: 'high',
          service: { name: 'storage' },
          created_at: '2026-03-01T00:00:00Z',
        },
      },
    });
    expect(alert.sourceProvider).toBe('pagerduty');
    expect(alert.severity).toBe('critical');
    expect(alert.service).toBe('storage');
  });

  it('normalizes OpsGenie payload', () => {
    const alert = normalizeOpsgenie({
      action: 'Create',
      alert: {
        alertId: 'og-789',
        message: 'Memory Leak',
        priority: 'P2',
        tags: ['service:api'],
      },
    });
    expect(alert.sourceProvider).toBe('opsgenie');
    expect(alert.severity).toBe('high');
    expect(alert.status).toBe('firing');
  });

  it('maps Datadog Recovered to resolved', () => {
    const alert = normalizeDatadog({ alert_transition: 'Recovered' });
    expect(alert.status).toBe('resolved');
  });

  it('maps OpsGenie Close to resolved', () => {
    const alert = normalizeOpsgenie({ action: 'Close', alert: {} });
    expect(alert.status).toBe('resolved');
  });
});
