import { z } from 'zod';
import crypto from 'node:crypto';
import pino from 'pino';

const logger = pino({ name: 'ingestion' });

// --- Canonical Alert Schema ---

export const canonicalAlertSchema = z.object({
  sourceProvider: z.enum(['datadog', 'pagerduty', 'opsgenie', 'grafana', 'custom']),
  sourceId: z.string(),
  fingerprint: z.string(),
  title: z.string(),
  severity: z.enum(['critical', 'high', 'medium', 'low', 'info']),
  status: z.enum(['firing', 'resolved']),
  service: z.string().optional(),
  environment: z.string().optional(),
  tags: z.record(z.string()).default({}),
  rawPayload: z.any(),
  timestamp: z.number(), // Unix ms
});

export type CanonicalAlert = z.infer<typeof canonicalAlertSchema>;

// --- HMAC Validation (BMad Must-Have: Replay Prevention) ---

const MAX_TIMESTAMP_DRIFT_SECONDS = 300; // 5 minutes

export interface HmacValidationResult {
  valid: boolean;
  error?: string;
}

export function validateDatadogHmac(
  body: string,
  signature: string | undefined,
  timestamp: string | undefined,
  secret: string,
): HmacValidationResult {
  if (!signature || !timestamp) {
    return { valid: false, error: 'Missing signature or timestamp header' };
  }

  // Timestamp freshness check
  const ts = parseInt(timestamp, 10);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - ts) > MAX_TIMESTAMP_DRIFT_SECONDS) {
    return { valid: false, error: 'stale timestamp' };
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return { valid: false, error: 'Invalid signature' };
  }

  return { valid: true };
}

export function validatePagerdutyHmac(
  body: string,
  signature: string | undefined,
  secret: string,
): HmacValidationResult {
  if (!signature) {
    return { valid: false, error: 'Missing signature header' };
  }

  // PagerDuty v1 signatures include timestamp in the signature header
  const parts = signature.split(',');
  const tsPart = parts.find(p => p.startsWith('t='));
  const sigPart = parts.find(p => p.startsWith('v1='));

  if (!tsPart || !sigPart) {
    return { valid: false, error: 'Malformed PagerDuty signature' };
  }

  const ts = parseInt(tsPart.slice(2), 10);
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - ts) > MAX_TIMESTAMP_DRIFT_SECONDS) {
    return { valid: false, error: 'stale timestamp' };
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${body}`)
    .digest('hex');

  const sig = sigPart.slice(3);
  if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return { valid: false, error: 'Invalid signature' };
  }

  return { valid: true };
}

export function validateOpsgenieHmac(
  body: string,
  signature: string | undefined,
  secret: string,
): HmacValidationResult {
  if (!signature) {
    return { valid: false, error: 'Missing signature header' };
  }

  // OpsGenie: extract timestamp from payload body
  let payload: any;
  try {
    payload = JSON.parse(body);
  } catch {
    return { valid: false, error: 'Invalid JSON body' };
  }

  const ts = payload?.timestamp;
  if (ts) {
    const tsSeconds = typeof ts === 'number' ? ts / 1000 : parseInt(ts, 10);
    const now = Math.floor(Date.now() / 1000);
    if (Math.abs(now - tsSeconds) > MAX_TIMESTAMP_DRIFT_SECONDS) {
      return { valid: false, error: 'stale timestamp' };
    }
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return { valid: false, error: 'Invalid signature' };
  }

  return { valid: true };
}

// --- Payload Normalizers ---

export function normalizeDatadog(payload: any): CanonicalAlert {
  return {
    sourceProvider: 'datadog',
    sourceId: payload.id ?? payload.alert_id ?? crypto.randomUUID(),
    fingerprint: payload.aggregation_key ?? payload.alert_id ?? '',
    title: payload.title ?? payload.msg_title ?? 'Datadog Alert',
    severity: mapDatadogPriority(payload.priority),
    status: payload.alert_transition === 'Recovered' ? 'resolved' : 'firing',
    service: payload.tags?.service,
    environment: payload.tags?.env,
    tags: payload.tags ?? {},
    rawPayload: payload,
    timestamp: payload.date_happened ? payload.date_happened * 1000 : Date.now(),
  };
}

export function normalizePagerduty(payload: any): CanonicalAlert {
  const event = payload.event ?? payload;
  const data = event.data ?? event.incident ?? {};
  return {
    sourceProvider: 'pagerduty',
    sourceId: data.id ?? crypto.randomUUID(),
    fingerprint: data.incident_key ?? data.id ?? '',
    title: data.title ?? data.description ?? 'PagerDuty Incident',
    severity: mapPagerdutyUrgency(data.urgency),
    status: event.event_type?.includes('resolve') ? 'resolved' : 'firing',
    service: data.service?.name,
    environment: data.body?.details?.environment,
    tags: {},
    rawPayload: payload,
    timestamp: data.created_at ? new Date(data.created_at).getTime() : Date.now(),
  };
}

export function normalizeOpsgenie(payload: any): CanonicalAlert {
  return {
    sourceProvider: 'opsgenie',
    sourceId: payload.alert?.alertId ?? crypto.randomUUID(),
    fingerprint: payload.alert?.alias ?? payload.alert?.alertId ?? '',
    title: payload.alert?.message ?? 'OpsGenie Alert',
    severity: mapOpsgeniePriority(payload.alert?.priority),
    status: payload.action === 'Close' ? 'resolved' : 'firing',
    service: payload.alert?.tags?.find((t: string) => t.startsWith('service:'))?.slice(8),
    tags: {},
    rawPayload: payload,
    timestamp: payload.alert?.createdAt ? new Date(payload.alert.createdAt).getTime() : Date.now(),
  };
}

// --- Severity Mappers ---

function mapDatadogPriority(p: string | undefined): CanonicalAlert['severity'] {
  switch (p) {
    case 'P1': return 'critical';
    case 'P2': return 'high';
    case 'P3': return 'medium';
    case 'P4': return 'low';
    default: return 'medium';
  }
}

function mapPagerdutyUrgency(u: string | undefined): CanonicalAlert['severity'] {
  switch (u) {
    case 'high': return 'critical';
    case 'low': return 'low';
    default: return 'medium';
  }
}

function mapOpsgeniePriority(p: string | undefined): CanonicalAlert['severity'] {
  switch (p) {
    case 'P1': return 'critical';
    case 'P2': return 'high';
    case 'P3': return 'medium';
    case 'P4': case 'P5': return 'low';
    default: return 'medium';
  }
}
