<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Verifies the SendGrid Event Webhook signature using the configured public key.
 * Signature scheme: https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features
 *
 * In local/dev environments without a configured public key, the check is
 * bypassed but a warning is logged.  Production must have
 * SENDGRID_WEBHOOK_PUBLIC_KEY set or webhooks are rejected.
 */
class VerifySendGridWebhook
{
    public function handle(Request $request, Closure $next): Response
    {
        $publicKey = config('services.sendgrid.webhook_public_key');

        if (empty($publicKey)) {
            if (app()->environment('production')) {
                return response('SendGrid webhook public key not configured.', 500);
            }
            logger()->warning('SendGrid webhook signature skipped (no key configured).');
            return $next($request);
        }

        $signature = $request->header('X-Twilio-Email-Event-Webhook-Signature');
        $timestamp = $request->header('X-Twilio-Email-Event-Webhook-Timestamp');
        $payload = $request->getContent();

        if (!$signature || !$timestamp) {
            return response('Missing SendGrid signature headers.', 403);
        }

        if (!$this->verify($publicKey, $signature, $timestamp, $payload)) {
            return response('Invalid SendGrid webhook signature.', 403);
        }

        return $next($request);
    }

    private function verify(string $publicKeyPem, string $signatureB64, string $timestamp, string $payload): bool
    {
        // SendGrid signs sha256(timestamp + payload) using ECDSA with their public key.
        if (!function_exists('openssl_verify')) {
            return false;
        }

        $signed = $timestamp . $payload;
        $signature = base64_decode($signatureB64, true);
        if ($signature === false) {
            return false;
        }

        $pem = $this->normalizePublicKey($publicKeyPem);
        $key = openssl_pkey_get_public($pem);
        if (!$key) {
            return false;
        }

        $result = openssl_verify($signed, $signature, $key, OPENSSL_ALGO_SHA256);

        return $result === 1;
    }

    private function normalizePublicKey(string $key): string
    {
        if (str_contains($key, 'BEGIN PUBLIC KEY')) {
            return $key;
        }

        $wrapped = chunk_split(trim($key), 64, "\n");
        return "-----BEGIN PUBLIC KEY-----\n{$wrapped}-----END PUBLIC KEY-----\n";
    }
}
