import crypto from 'crypto';
import redis from '../redis.js';
import { getPool } from '../db.js';

const RETRY_DELAYS_MS = [1000, 4000, 16000];
const DLQ_TTL_SECONDS = 604800; // 7 days

export interface WebhookPayload {
  customerId: string;
  platform: string;
  event: string;
  data: Record<string, unknown>;
}

export function isValidWebhookUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    if (!['http:', 'https:'].includes(parsed.protocol)) return false;
    const host = parsed.hostname;
    if (host === 'localhost' || host === '0.0.0.0' || host.endsWith('.local')) return false;
    // Block RFC-1918 private ranges and loopback
    if (/^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(host)) return false;
    return true;
  } catch {
    return false;
  }
}

function signPayload(secret: string, payload: string): string {
  return `sha256=${crypto.createHmac('sha256', secret).update(payload).digest('hex')}`;
}

async function postWithRetry(url: string, payload: string, signature: string): Promise<boolean> {
  for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
    if (attempt > 0) {
      await new Promise(r => setTimeout(r, RETRY_DELAYS_MS[attempt - 1]));
    }
    try {
      const res = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'X-SquareMCP-Signature': signature },
        body: payload,
        signal: AbortSignal.timeout(10_000),
      });
      if (res.ok) return true;
      console.warn(`[webhook] attempt ${attempt + 1} HTTP ${res.status} → ${url}`);
    } catch (err) {
      console.warn(`[webhook] attempt ${attempt + 1} error:`, (err as Error).message);
    }
  }
  return false;
}

export async function deliverWebhook(
  customerId: string,
  platform: string,
  event: string,
  data: Record<string, unknown>
): Promise<void> {
  const [rows] = await getPool().query<any[]>(
    'SELECT webhook_url, webhook_secret FROM customers WHERE id = ?',
    [customerId]
  );
  if (!rows.length || !rows[0].webhook_url || !rows[0].webhook_secret) return;

  const { webhook_url, webhook_secret } = rows[0] as { webhook_url: string; webhook_secret: string };
  const payload: WebhookPayload = { customerId, platform, event, data };
  const payloadStr = JSON.stringify(payload);
  const signature = signPayload(webhook_secret, payloadStr);

  const delivered = await postWithRetry(webhook_url, payloadStr, signature);

  if (!delivered) {
    console.error(`[webhook] all attempts failed for customer ${customerId}, pushing to DLQ`);
    const dlqKey = `webhook:dlq:${customerId}`;
    const entry = JSON.stringify({
      payload,
      failedAt: new Date().toISOString(),
      attempts: RETRY_DELAYS_MS.length + 1,
    });
    await redis.rPush(dlqKey, entry);
    await redis.expire(dlqKey, DLQ_TTL_SECONDS);
  }
}
