<?php

namespace Kylin\Adminlte\Middleware;

use Closure;
use Illuminate\Http\Request;

/**
 * 后台管理安全头中间件
 *
 * 依据等保 2.0 (GB/T 22239-2019) 8.1.3.2 通信保密性要求和
 * OWASP 安全头最佳实践，为后台管理响应添加安全响应头。
 *
 * 注册方式：
 *  在 App\Http\Kernel 中注册为路由中间件：
 *  'admin-security' => \Kylin\Adminlte\Middleware\SecurityHeadersMiddleware::class,
 *
 *  或在路由组中使用：
 *  Route::middleware('admin-security')->group(function () { ... });
 */
class SecurityHeadersMiddleware
{
    /**
     * 处理请求并添加安全响应头
     *
     * @param  Request  $request
     * @param  Closure  $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next): mixed
    {
        $response = $next($request);

        // 仅对 HTML 响应添加安全头
        if (! $this->isHtmlResponse($response)) {
            return $response;
        }

        $this->addSecurityHeaders($response);

        return $response;
    }

    /**
     * 判断是否为 HTML 响应
     */
    protected function isHtmlResponse($response): bool
    {
        $contentType = $response->headers->get('Content-Type', '');

        return str_contains($contentType, 'text/html');
    }

    /**
     * 添加安全响应头
     */
    protected function addSecurityHeaders($response): void
    {
        // CSP: 内容安全策略 — 依据等保 2.0 8.1.3.2，防止 XSS/数据注入
        $response->headers->set('Content-Security-Policy', $this->buildCspHeader());

        // X-Frame-Options: 防止点击劫持
        $response->headers->set('X-Frame-Options', 'SAMEORIGIN');

        // X-Content-Type-Options: 防止 MIME 类型嗅探
        $response->headers->set('X-Content-Type-Options', 'nosniff');

        // Referrer-Policy: 控制 Referer 信息
        $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');

        // Permissions-Policy: 限制浏览器功能使用
        $response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');

        // 强制 HTTPS — 依据等保 2.0 8.1.3.2
        if (config('kylin-adminlte.security.force_https', true)) {
            $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        }
    }

    /**
     * 构建 CSP 头
     */
    protected function buildCspHeader(): string
    {
        $csp = config('kylin-adminlte.security.csp', []);

        if (empty($csp)) {
            // 默认 CSP 策略
            $csp = [
                'default-src' => "'self'",
                'script-src' => "'self' 'unsafe-inline' 'unsafe-eval'",
                'style-src' => "'self' 'unsafe-inline'",
                'img-src' => "'self' data: blob:",
                'font-src' => "'self' data:",
                'connect-src' => "'self'",
                'frame-src' => "'self'",
                'object-src' => "'none'",
                'base-uri' => "'self'",
                'form-action' => "'self'",
            ];
        }

        $parts = [];
        foreach ($csp as $directive => $value) {
            $parts[] = "{$directive} {$value}";
        }

        return implode('; ', $parts);
    }
}