import { NextRequest, NextResponse } from 'next/server';
import { checkRateLimit, RATE_LIMIT_WRITE } from '../../../lib/rate-limit';

/**
 * POST /api/agent/webhooks — Register a webhook for event notifications
 * GET  /api/agent/webhooks — List your registered webhooks
 * DELETE /api/agent/webhooks — Remove a webhook
 *
 * Agents subscribe to events instead of polling.
 * Webhooks fire when: bond created, dispute filed, reputation updated,
 * credential issued, bridge synced, insurance claim filed.
 *
 * In-memory store for now — production would use a persistent store.
 */

interface WebhookRegistration {
  id: string;
  address: string;
  url: string;
  events: string[];
  chain: string;
  createdAt: string;
  active: boolean;
  secret: string;
}

const VALID_EVENTS = [
  'bond.created',
  'bond.activated',
  'bond.completed',
  'reputation.updated',
  'credential.issued',
  'credential.revoked',
  'dispute.filed',
  'dispute.resolved',
  'bridge.synced',
  'insurance.claim_filed',
  'insurance.payout',
  'agent.registered',
  'vouch.received',
  'vouch.slashed',
  'task.assigned',
  'task.completed',
];

// In-memory store (Vercel serverless — ephemeral, but demonstrates the API contract)
const webhooks = new Map<string, WebhookRegistration>();

function generateId(): string {
  return 'wh_' + Array.from(crypto.getRandomValues(new Uint8Array(12)))
    .map(b => b.toString(16).padStart(2, '0')).join('');
}

function generateSecret(): string {
  return 'whsec_' + Array.from(crypto.getRandomValues(new Uint8Array(24)))
    .map(b => b.toString(16).padStart(2, '0')).join('');
}


function requireWebhookWriteAuthorization(req: NextRequest): NextResponse | null {
  const token = process.env.WEBHOOK_ADMIN_TOKEN;
  if (!token) {
    return NextResponse.json(
      { error: 'webhook write service is not configured', code: 'webhook_auth_unconfigured' },
      { status: 503, headers: { 'Cache-Control': 'no-store' } },
    );
  }
  if (req.headers.get('authorization') !== `Bearer ${token}`) {
    return NextResponse.json({ error: 'unauthorized' }, { status: 401, headers: { 'Cache-Control': 'no-store' } });
  }
  return null;
}

// ---------------------------------------------------------------------------
// VF-08: SSRF prevention — validate webhook URLs before registering
// ---------------------------------------------------------------------------

const PRIVATE_IP_PATTERNS = [
  /^127\./,                          // loopback
  /^10\./,                           // RFC-1918 class A
  /^172\.(1[6-9]|2\d|3[01])\./,     // RFC-1918 class B
  /^192\.168\./,                     // RFC-1918 class C
  /^169\.254\./,                     // link-local / cloud metadata (AWS, GCP, Azure IMDS)
  /^0\.0\.0\.0/,                     // unspecified
  /^::1$/,                           // IPv6 loopback
  /^fc00:/i,                         // IPv6 unique local (fc00::/7 lower half)
  /^fd[0-9a-f]{2}:/i,               // IPv6 unique local (fc00::/7 upper half)
  /^fe80:/i,                         // IPv6 link-local
  /^\[?fd00:/i,                      // IPv6 fd00:: range (bracket-enclosed or bare)
];

function isSafeWebhookUrl(rawUrl: string): { safe: boolean; reason?: string } {
  let parsed: URL;
  try {
    parsed = new URL(rawUrl);
  } catch {
    return { safe: false, reason: 'Invalid URL format' };
  }

  if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
    return { safe: false, reason: 'URL must use http or https scheme' };
  }

  const hostname = parsed.hostname.toLowerCase();

  if (hostname === 'localhost') {
    return { safe: false, reason: 'Localhost URLs are not permitted for webhooks' };
  }

  for (const pattern of PRIVATE_IP_PATTERNS) {
    if (pattern.test(hostname)) {
      return { safe: false, reason: 'Private/internal IP addresses are not permitted for webhooks' };
    }
  }

  return { safe: true };
}

// VF-05: CORS is intentionally permissive (*) for all routes because this is an
// agent-first protocol consumed by external autonomous agents that may operate
// from any origin. Restricting CORS would break legitimate agent integrations.

export async function POST(req: NextRequest) {
  const authorizationFailure = requireWebhookWriteAuthorization(req);
  if (authorizationFailure) return authorizationFailure;
  // VF-02: Rate limiting — 20 POST requests per minute per IP
  const rateLimitResponse = checkRateLimit(req, RATE_LIMIT_WRITE);
  if (rateLimitResponse) return rateLimitResponse;

  try {
    const body = await req.json();
    const { address, url, events, chain } = body;

    if (!address || typeof address !== 'string' || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
      return NextResponse.json({ error: 'address is required (0x...)' }, { status: 400 });
    }

    // VF-08: SSRF prevention — validate URL safety before registering
    if (!url || typeof url !== 'string') {
      return NextResponse.json({ error: 'url is required' }, { status: 400 });
    }
    const urlCheck = isSafeWebhookUrl(url);
    if (!urlCheck.safe) {
      return NextResponse.json(
        { error: `Invalid webhook URL: ${urlCheck.reason}` },
        { status: 400 },
      );
    }
    if (!events || !Array.isArray(events) || events.length === 0) {
      return NextResponse.json(
        { error: `events array is required. Valid events: ${VALID_EVENTS.join(', ')}` },
        { status: 400 },
      );
    }
    const invalidEvents = events.filter((e: string) => !VALID_EVENTS.includes(e));
    if (invalidEvents.length > 0) {
      return NextResponse.json(
        { error: `Invalid events: ${invalidEvents.join(', ')}. Valid: ${VALID_EVENTS.join(', ')}` },
        { status: 400 },
      );
    }

    const id = generateId();
    const secret = generateSecret();
    const registration: WebhookRegistration = {
      id,
      address: address.toLowerCase(),
      url,
      events,
      chain: chain || 'all',
      createdAt: new Date().toISOString(),
      active: true,
      secret,
    };

    webhooks.set(id, registration);

    return NextResponse.json({
      id,
      url,
      events,
      chain: registration.chain,
      active: true,
      secret,
      message: 'Webhook registered. Store the secret — it will be sent as X-Vaultfire-Signature header on each delivery for verification.',
      note: 'Webhook storage is currently ephemeral (in-memory). Persistent webhook delivery is coming in v3.4.',
    }, { status: 201 });
  } catch {
    return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 });
  }
}

export async function GET(req: NextRequest) {
  const authorizationFailure = requireWebhookWriteAuthorization(req);
  if (authorizationFailure) return authorizationFailure;
  const { searchParams } = new URL(req.url);
  const address = searchParams.get('address');

  if (!address) {
    return NextResponse.json({
      validEvents: VALID_EVENTS,
      registeredCount: webhooks.size,
      usage: {
        register: 'POST /api/agent/webhooks with { address, url, events, chain? }',
        list: 'GET /api/agent/webhooks?address=0x...',
        delete: 'DELETE /api/agent/webhooks?id=wh_...',
      },
    });
  }

  const userWebhooks = Array.from(webhooks.values())
    .filter(w => w.address === address.toLowerCase())
    .map(({ secret, ...rest }) => rest); // Don't expose secret in list

  return NextResponse.json({
    address,
    webhooks: userWebhooks,
    count: userWebhooks.length,
  });
}

export async function DELETE(req: NextRequest) {
  const authorizationFailure = requireWebhookWriteAuthorization(req);
  if (authorizationFailure) return authorizationFailure;
  // VF-02: Rate limiting — 20 requests per minute per IP
  const rateLimitResponse = checkRateLimit(req, RATE_LIMIT_WRITE);
  if (rateLimitResponse) return rateLimitResponse;

  const { searchParams } = new URL(req.url);
  const id = searchParams.get('id');

  if (!id) {
    return NextResponse.json({ error: 'id query parameter required (e.g. ?id=wh_...)' }, { status: 400 });
  }

  // Operator authentication above authorizes deletion; request-body addresses are not credentials.

  webhooks.delete(id);
  return NextResponse.json({ deleted: true, id });
}
