import { NextResponse } from "next/server";
import crypto from "crypto";
import { pool } from "@/lib/db";

/**
 * Validates a webhook URL to prevent SSRF attacks.
 * Blocks:
 * - Non-HTTPS URLs
 * - localhost and 127.0.0.1
 * - Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
 * - Link-local IPv6 addresses (fc00::/7, fe80::/10)
 * - AWS metadata endpoint (169.254.169.254)
 */
function validateWebhookUrl(url: string): boolean {
  try {
    const parsed = new URL(url);

    // Only allow HTTPS URLs
    if (parsed.protocol !== "https:") {
      return false;
    }

    const hostname = parsed.hostname.toLowerCase();

    // Block localhost and loopback
    if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1") {
      return false;
    }

    // Block private IP ranges using regex patterns
    const blockedPatterns = [
      // 10.0.0.0/8 - Class A private network
      /^10\./,
      // 172.16.0.0/12 - Class B private network (172.16.x.x - 172.31.x.x)
      /^172\.(1[6-9]|2[0-9]|3[01])\./,
      // 192.168.0.0/16 - Class C private network
      /^192\.168\./,
      // AWS metadata endpoint
      /^169\.254\.169\.254$/,
      // Link-local IPv6 (fc00::/7 and fe80::/10)
      /^fc00:/i,
      /^fe80:/i,
    ];

    for (const pattern of blockedPatterns) {
      if (pattern.test(hostname)) {
        return false;
      }
    }

    return true;
  } catch {
    // Invalid URL format
    return false;
  }
}

// POST /api/wholesale/webhooks/dispatch
// Processes pending webhook events from wholesale_sync_log and dispatches to configured URLs.
// Called by a cron job or manually after enqueue_wholesale_webhook has queued events.
export async function POST() {
  if (!process.env.DATABASE_URL) {
    return NextResponse.json({ error: "Server misconfiguration" }, { status: 500 });
  }

  // Fetch pending webhooks
  const pendingRes = await pool.query<{
    id: string;
    brand_id: string;
    event_type: string;
    order_id: string | null;
    payload: Record<string, unknown> | null;
    attempts: number;
    url: string;
    secret: string;
  }>(
    "SELECT * FROM get_pending_webhooks($1)",
    [10]
  );

  if (pendingRes.rows.length === 0) {
    return NextResponse.json({ message: "No pending webhooks.", dispatched: 0 });
  }

  const pending = pendingRes.rows;
  let dispatched = 0;

  for (const webhook of pending) {
    // Validate URL to prevent SSRF attacks
    if (!validateWebhookUrl(webhook.url)) {
      console.error("[SSRF_BLOCKED]", {
        webhookId: webhook.id,
        brandId: webhook.brand_id,
        url: webhook.url,
        eventType: webhook.event_type,
        timestamp: new Date().toISOString(),
      });
      await markFailed(webhook.id, "SSRF attempt blocked: invalid webhook URL");
      continue;
    }

    const payload = webhook.payload ?? {};
    const payloadString = JSON.stringify(payload);

    // Build HMAC-SHA256 signature: HMAC(secret, payload_string)
    const signature = crypto
      .createHmac("sha256", webhook.secret)
      .update(payloadString)
      .digest("hex");

    try {
      const res = await fetch(webhook.url, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "X-Webhook-Signature": `sha256=${signature}`,
          "X-Webhook-Event": webhook.event_type,
        },
        body: payloadString,
      });

      const responseText = await res.text().catch(() => "");

      if (res.ok) {
        await markSent(webhook.id, `HTTP ${res.status}: ${responseText.slice(0, 200)}`);
        dispatched++;
      } else {
        await markFailed(webhook.id, `HTTP ${res.status}: ${responseText.slice(0, 200)}`);
      }
    } catch (err) {
      const msg = err instanceof Error ? err.message : "Network error";
      await markFailed(webhook.id, msg);
    }
  }

  return NextResponse.json({ message: `Dispatched ${dispatched}/${pending.length} webhook(s).`, dispatched });
}

async function markSent(logId: string, response: string) {
  try {
    await pool.query(
      "SELECT mark_webhook_sent($1, $2)",
      [logId, response]
    );
  } catch {
    // best-effort
  }
}

async function markFailed(logId: string, response: string) {
  try {
    await pool.query(
      "SELECT mark_webhook_failed($1, $2)",
      [logId, response]
    );
  } catch {
    // best-effort
  }
}

export async function GET() {
  return NextResponse.json({
    description: "POST to process pending webhook events",
    events: ["order_created", "order_fulfilled", "deposit_recorded", "order_paid"],
  });
}
