<?php

declare(strict_types=1);

namespace BotGuard\Middleware;

use BotGuard\Core\Request;
use BotGuard\Core\Response;
use Closure;

/**
 * Attaches a hardened set of security headers to every response.
 *
 * Includes a Content-Security-Policy, anti-clickjacking, MIME-sniffing and
 * referrer controls. HSTS is only emitted over HTTPS. The CSP allows the CDN
 * origins used by the frontend (Bootstrap, Chart.js) plus inline styles/scripts
 * required by the verification page bootstrap.
 */
final class SecurityHeadersMiddleware implements MiddlewareInterface
{
    public function handle(Request $request, Closure $next): Response
    {
        /** @var Response $response */
        $response = $next($request);

        // CAPTCHA provider origins (reCAPTCHA, hCaptcha, Turnstile). Kept in one
        // list so script/frame/style/connect directives stay consistent.
        $captchaScript = 'https://www.google.com https://www.gstatic.com https://www.recaptcha.net https://js.hcaptcha.com https://*.hcaptcha.com https://challenges.cloudflare.com';
        $captchaFrame  = 'https://www.google.com https://recaptcha.net https://www.recaptcha.net https://hcaptcha.com https://*.hcaptcha.com https://challenges.cloudflare.com';
        $captchaConnect = 'https://www.google.com https://*.hcaptcha.com https://hcaptcha.com https://challenges.cloudflare.com';

        $csp = implode('; ', [
            "default-src 'self'",
            "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net {$captchaScript}",
            "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://*.hcaptcha.com",
            "font-src 'self' https://cdn.jsdelivr.net data:",
            "img-src 'self' data: https:",
            "connect-src 'self' {$captchaConnect}",
            "frame-src 'self' {$captchaFrame}",
            "object-src 'none'",
            "base-uri 'self'",
            "form-action 'self'",
            "frame-ancestors 'self'",
        ]);

        $response
            ->withHeader('X-Content-Type-Options', 'nosniff')
            ->withHeader('X-Frame-Options', 'SAMEORIGIN')
            ->withHeader('Referrer-Policy', 'strict-origin-when-cross-origin')
            ->withHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
            ->withHeader('X-XSS-Protection', '0')
            ->withHeader('Content-Security-Policy', $csp);

        $https = (! empty($request->server('HTTPS')) && $request->server('HTTPS') !== 'off')
            || $request->header('X-Forwarded-Proto') === 'https';

        if ($https) {
            $response->withHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        }

        return $response;
    }
}
