<?php
/**
 * HMAC Request Signature Middleware
 * 
 * Verifies that incoming requests are signed with a shared secret,
 * preventing replay attacks and ensuring request integrity.
 * 
 * Client must send:
 *   X-Timestamp: Unix timestamp (seconds)
 *   X-HMAC-Signature: HMAC-SHA256(timestamp + "." + raw_body, HMAC_SECRET_KEY)
 */

declare(strict_types=1);

namespace App\Middleware;

use App\Core\Security;

final class HmacMiddleware
{
    /**
     * @param int $maxAgeSeconds Max age for replay attack window (default: 5 minutes)
     */
    public static function verify(int $maxAgeSeconds = 300): void
    {
        $headers   = getallheaders();
        $signature = $headers['X-HMAC-Signature'] ?? $headers['x-hmac-signature'] ?? '';
        $timestamp = $headers['X-Timestamp']      ?? $headers['x-timestamp']      ?? '';

        // 1. Ensure both headers are present
        if (empty($signature) || empty($timestamp)) {
            json_error('Missing HMAC signature or timestamp', 401);
        }

        // 2. Validate timestamp is numeric
        if (!ctype_digit((string)$timestamp)) {
            json_error('Invalid timestamp format', 401);
        }

        // 3. Replay attack prevention — reject stale requests
        $age = abs(time() - (int)$timestamp);
        if ($age > $maxAgeSeconds) {
            json_error('Request expired. Check your system clock.', 401);
        }

        // 4. Build the expected signature
        $body    = file_get_contents('php://input');
        $payload = $timestamp . '.' . $body;
        $secret  = env('HMAC_SECRET_KEY');

        if (!$secret || strlen($secret) < 32) {
            error_log('FATAL: HMAC_SECRET_KEY is missing or too short in .env');
            json_error('Server configuration error', 500);
        }

        // 5. Verify using constant-time comparison (prevents timing attacks)
        if (!Security::verifySignature($payload, $signature, $secret)) {
            error_log("HMAC verification failed for " . ($_SERVER['REQUEST_URI'] ?? ''));
            json_error('Invalid request signature', 401);
        }
    }
}
