/**
 * PagerDuty Channel (PR-27 / P3-10, May 2026).
 *
 * Sends a trigger event to PagerDuty's Events API v2. The integration
 * key (routing_key) is per-service and travels in the channel config —
 * each PagerDuty integration represents a separate service so the
 * on-call schedule + escalation policy attached on PagerDuty's side
 * determines who gets paged.
 *
 * Config shape (ChannelConfig.pagerduty):
 *   { routingKey: string, severity?: 'info' | 'warning' | 'error' | 'critical' }
 *
 * SSRF: PagerDuty's events.pagerduty.com endpoint is on the
 * safeOutboundFetch allow-list so we don't have to re-resolve DNS
 * every time. If the operator decides to use a self-hosted Events
 * API proxy (rare), they'd add that hostname to the allow-list too.
 */
import type { NotificationChannel, ChannelConfig } from '../types';
import { log } from '@/lib/observability/logger';
import { captureException } from '@/lib/observability/sentry';
import { safeOutboundFetch } from '@/lib/network-security/safe-fetch';
import { redactHighEntropy } from '../redact';

const PAGERDUTY_EVENTS_API = 'https://events.pagerduty.com/v2/enqueue';

type Severity = 'info' | 'warning' | 'error' | 'critical';

export class PagerDutyChannel implements NotificationChannel {
    readonly type = 'pagerduty' as const;

    async send(message: string, config: ChannelConfig): Promise<void> {
        try {
            await this.sendOrThrow(message, config);
        } catch (error) {
            log.error({ err: error, channel: 'pagerduty' }, 'PagerDutyChannel send failed');
            captureException(error, { channel: 'pagerduty' });
        }
    }

    async sendOrThrow(message: string, config: ChannelConfig): Promise<void> {
        const routingKey = (config as { routingKey?: string }).routingKey;
        if (!routingKey || typeof routingKey !== 'string' || routingKey.length < 8) {
            throw new Error('PagerDuty channel requires a valid routingKey (Integration Key from a service in PagerDuty).');
        }

        const severity: Severity = ((config as { severity?: string }).severity as Severity) ?? 'error';
        if (!['info', 'warning', 'error', 'critical'].includes(severity)) {
            throw new Error(`PagerDuty severity must be info|warning|error|critical, got "${severity}".`);
        }

        const summary = message.length > 1024 ? message.slice(0, 1021) + '...' : message;
        const body = {
            routing_key: routingKey,
            event_action: 'trigger' as const,
            client: 'Uptime Sentinel',
            payload: {
                summary,
                severity,
                source: process.env.NEXTAUTH_URL ?? 'uptime-sentinel',
                timestamp: new Date().toISOString(),
            },
        };

        const response = await safeOutboundFetch(PAGERDUTY_EVENTS_API, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
            body: JSON.stringify(body),
        }, { allowedHosts: ['events.pagerduty.com'] });

        if (!response.ok) {
            const text = await response.text().catch(() => '<unreadable>');
            // AUDIT-2 #9 (2026-05-24): redact high-entropy substrings.
            throw new Error(`PagerDuty ${response.status}: ${redactHighEntropy(text).slice(0, 256)}`);
        }
    }
}
