import { z } from 'zod';
import pino from 'pino';

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

export interface DriftNotification {
  tenantId: string;
  stackName: string;
  driftScore: number;
  criticalCount: number;
  highCount: number;
  totalDrifted: number;
  totalResources: number;
  reportUrl: string;
}

// --- Slack ---

const slackBlockKit = (n: DriftNotification) => ({
  blocks: [
    {
      type: 'header',
      text: { type: 'plain_text', text: `🔍 Drift Detected: ${n.stackName}` },
    },
    {
      type: 'section',
      fields: [
        { type: 'mrkdwn', text: `*Drift Score:* ${n.driftScore}/100` },
        { type: 'mrkdwn', text: `*Drifted:* ${n.totalDrifted}/${n.totalResources} resources` },
        { type: 'mrkdwn', text: `*Critical:* ${n.criticalCount}` },
        { type: 'mrkdwn', text: `*High:* ${n.highCount}` },
      ],
    },
    {
      type: 'actions',
      elements: [
        {
          type: 'button',
          text: { type: 'plain_text', text: '📋 View Report' },
          url: n.reportUrl,
          style: 'primary',
        },
        {
          type: 'button',
          text: { type: 'plain_text', text: '🔧 Remediate' },
          action_id: `remediate_${n.stackName}`,
          style: 'danger',
          confirm: {
            title: { type: 'plain_text', text: 'Generate Remediation Plan?' },
            text: { type: 'mrkdwn', text: `This will run \`terraform plan\` for *${n.stackName}*` },
            confirm: { type: 'plain_text', text: 'Yes, plan it' },
            deny: { type: 'plain_text', text: 'Cancel' },
          },
        },
        {
          type: 'button',
          text: { type: 'plain_text', text: '✅ Accept Drift' },
          action_id: `accept_${n.stackName}`,
        },
      ],
    },
  ],
});

export async function sendSlackNotification(webhookUrl: string, notification: DriftNotification): Promise<void> {
  const payload = slackBlockKit(notification);

  const resp = await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });

  if (!resp.ok) {
    const text = await resp.text();
    // Slack 429 — respect retry-after
    if (resp.status === 429) {
      const retryAfter = parseInt(resp.headers.get('retry-after') ?? '5');
      logger.warn({ retryAfter }, 'Slack rate limited — backing off');
      throw new SlackRateLimitError(retryAfter);
    }
    throw new Error(`Slack webhook failed: ${resp.status} ${text}`);
  }
}

export class SlackRateLimitError extends Error {
  retryAfterSeconds: number;
  constructor(retryAfter: number) {
    super(`Slack rate limited, retry after ${retryAfter}s`);
    this.retryAfterSeconds = retryAfter;
  }
}

// --- Webhook ---

export async function sendWebhookNotification(url: string, notification: DriftNotification): Promise<void> {
  const resp = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      event: 'drift.detected',
      ...notification,
      timestamp: new Date().toISOString(),
    }),
  });

  if (!resp.ok) {
    throw new Error(`Webhook delivery failed: ${resp.status}`);
  }
}

// --- Email (Resend) ---

export async function sendEmailNotification(
  apiKey: string,
  from: string,
  to: string,
  notification: DriftNotification,
): Promise<void> {
  const severityEmoji = notification.criticalCount > 0 ? '🔴' : notification.highCount > 0 ? '🟠' : '🟡';
  const subject = `${severityEmoji} Drift detected: ${notification.stackName} (score ${notification.driftScore}/100)`;

  const html = `
    <div style="font-family: -apple-system, sans-serif; max-width: 600px;">
      <h2>Drift Detected: ${notification.stackName}</h2>
      <table style="border-collapse: collapse; width: 100%;">
        <tr><td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Drift Score</strong></td><td style="padding: 8px; border-bottom: 1px solid #eee;">${notification.driftScore}/100</td></tr>
        <tr><td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Drifted Resources</strong></td><td style="padding: 8px; border-bottom: 1px solid #eee;">${notification.totalDrifted} of ${notification.totalResources}</td></tr>
        <tr><td style="padding: 8px; border-bottom: 1px solid #eee;"><strong>Critical</strong></td><td style="padding: 8px; border-bottom: 1px solid #eee;">${notification.criticalCount}</td></tr>
        <tr><td style="padding: 8px;"><strong>High</strong></td><td style="padding: 8px;">${notification.highCount}</td></tr>
      </table>
      <p style="margin-top: 16px;"><a href="${notification.reportUrl}" style="background: #6366f1; color: white; padding: 10px 20px; border-radius: 6px; text-decoration: none;">View Report</a></p>
    </div>
  `;

  const resp = await fetch('https://api.resend.com/emails', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ from, to: [to], subject, html }),
  });

  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`Resend email failed: ${resp.status} ${text}`);
  }

  logger.info({ to, stackName: notification.stackName }, 'Email notification sent via Resend');
}

// --- PagerDuty Events API v2 ---

export async function sendPagerDutyNotification(
  routingKey: string,
  notification: DriftNotification,
  maxSeverity: string,
): Promise<void> {
  const pdSeverity = maxSeverity === 'critical' ? 'critical' : maxSeverity === 'high' ? 'error' : 'warning';

  const payload = {
    routing_key: routingKey,
    event_action: 'trigger',
    dedup_key: `dd0c-drift-${notification.tenantId}-${notification.stackName}`,
    payload: {
      summary: `Drift detected in ${notification.stackName}: ${notification.totalDrifted}/${notification.totalResources} resources drifted (score ${notification.driftScore}/100)`,
      severity: pdSeverity,
      source: 'dd0c/drift',
      component: notification.stackName,
      group: 'infrastructure-drift',
      custom_details: {
        drift_score: notification.driftScore,
        critical_count: notification.criticalCount,
        high_count: notification.highCount,
        total_drifted: notification.totalDrifted,
        total_resources: notification.totalResources,
        report_url: notification.reportUrl,
      },
    },
    links: [{ href: notification.reportUrl, text: 'View Drift Report' }],
  };

  const resp = await fetch('https://events.pagerduty.com/v2/enqueue', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });

  if (!resp.ok) {
    const text = await resp.text();
    throw new Error(`PagerDuty event failed: ${resp.status} ${text}`);
  }

  logger.info({ stackName: notification.stackName, severity: pdSeverity }, 'PagerDuty event triggered');
}

// --- Dispatcher ---

export interface NotificationChannel {
  channel: 'slack' | 'email' | 'webhook' | 'pagerduty';
  config: Record<string, string>;
  minSeverity: string;
  enabled: boolean;
}

const severityRank: Record<string, number> = {
  low: 1,
  medium: 2,
  high: 3,
  critical: 4,
};

export function shouldNotify(channel: NotificationChannel, driftScore: number, maxSeverity: string): boolean {
  if (!channel.enabled) return false;
  const channelRank = severityRank[channel.minSeverity] ?? 2;
  const eventRank = severityRank[maxSeverity] ?? 2;
  return eventRank >= channelRank;
}

export async function dispatchNotifications(
  channels: NotificationChannel[],
  notification: DriftNotification,
  maxSeverity: string,
): Promise<void> {
  for (const ch of channels) {
    if (!shouldNotify(ch, notification.driftScore, maxSeverity)) continue;

    try {
      switch (ch.channel) {
        case 'slack':
          await sendSlackNotification(ch.config.webhook_url!, notification);
          break;
        case 'webhook':
          await sendWebhookNotification(ch.config.url!, notification);
          break;
        case 'email':
          await sendEmailNotification(ch.config.api_key!, ch.config.from!, ch.config.to!, notification);
          break;
        case 'pagerduty':
          await sendPagerDutyNotification(ch.config.routing_key!, notification, maxSeverity);
          break;
      }
    } catch (err) {
      if (err instanceof SlackRateLimitError) {
        // Schedule retry with exponential backoff
        const delayMs = err.retryAfterSeconds * 1000;
        logger.warn({ retryAfter: err.retryAfterSeconds }, 'Slack rate limited — scheduling retry');
        setTimeout(() => {
          sendSlackNotification(ch.config.webhook_url!, notification).catch((retryErr) => {
            logger.error({ error: (retryErr as Error).message }, 'Slack retry failed');
          });
        }, delayMs);
      } else {
        logger.error({ channel: ch.channel, error: (err as Error).message }, 'Notification delivery failed');
      }
    }
  }
}
