<?php

/**
 * 文件功能：在可信代理场景下解析客户端真实 IP。
 *
 * 仅当当前请求明确来自配置中的反向代理 / CDN 节点时，
 * 才会采信其透传的真实客户端 IP 头，避免外部客户端伪造来源。
 */

namespace App\Http\Middleware;

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

/**
 * 类功能：为可信代理请求恢复真实客户端 IP。
 */
class CloudflareProxies
{
    /**
     * 处理进入应用的请求，并在可信代理场景下覆写客户端 IP。
     */
    public function handle(Request $request, Closure $next): Response
    {
        $realIp = $this->resolveTrustedClientIp($request);

        if (! empty($realIp)) {
            // 仅在确认上游代理可信且透传 IP 合法时，才覆写 request()->ip() 的来源。
            $request->server->set('REMOTE_ADDR', $realIp);
            $request->headers->set('X-Forwarded-For', $realIp);
        }

        return $next($request);
    }

    /**
     * 从可信代理头中解析真实客户端 IP。
     */
    private function resolveTrustedClientIp(Request $request): ?string
    {
        $remoteAddress = (string) $request->server->get('REMOTE_ADDR', '');
        if (! $this->isTrustedProxy($remoteAddress)) {
            return null;
        }

        foreach (['CF-Connecting-IP', 'EO-Client-IP', 'X-Real-IP'] as $headerName) {
            $resolvedIp = $this->sanitizeIp($request->header($headerName));
            if ($resolvedIp !== null) {
                return $resolvedIp;
            }
        }

        return $this->extractForwardedForIp($request->header('X-Forwarded-For'));
    }

    /**
     * 判断当前请求是否来自受信代理节点。
     */
    private function isTrustedProxy(string $remoteAddress): bool
    {
        if ($this->sanitizeIp($remoteAddress) === null) {
            return false;
        }

        $trustedProxies = config('app.trusted_proxies', ['127.0.0.1', '::1']);
        foreach ($trustedProxies as $trustedProxy) {
            $trustedProxy = trim((string) $trustedProxy);
            if ($trustedProxy !== '' && IpUtils::checkIp($remoteAddress, $trustedProxy)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 从 X-Forwarded-For 头中提取最左侧的合法 IP。
     */
    private function extractForwardedForIp(?string $forwardedFor): ?string
    {
        if (! is_string($forwardedFor) || $forwardedFor === '') {
            return null;
        }

        foreach (explode(',', $forwardedFor) as $candidateIp) {
            $resolvedIp = $this->sanitizeIp($candidateIp);
            if ($resolvedIp !== null) {
                return $resolvedIp;
            }
        }

        return null;
    }

    /**
     * 校验并标准化 IP 文本。
     */
    private function sanitizeIp(?string $ip): ?string
    {
        $normalizedIp = trim((string) $ip);

        return filter_var($normalizedIp, FILTER_VALIDATE_IP) ? $normalizedIp : null;
    }
}
