<?php

namespace Kylin\Core\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Kylin\Core\ApiResponse;
use Kylin\Core\Enums\CoreErrorCode;
use Symfony\Component\HttpFoundation\Response;

/**
 * 请求频率限制中间件
 *
 * 基于 Laravel 内置 RateLimiter 实现 API 限流保护。
 * 支持按用户或 IP 维度进行限流，并返回标准限流响应头。
 *
 * 使用方式：
 *  Route::middleware('kylin.rate_limit:60,1') —— 每分钟最多 60 次请求
 *
 * 响应头：
 *  - X-RateLimit-Limit：最大请求数
 *  - X-RateLimit-Remaining：剩余可用请求数
 *  - X-RateLimit-Reset：重置时间戳
 *  - Retry-After：限流后等待秒数（仅在触发限流时返回）
 */
class RateLimit
{
    /**
     * 处理请求
     *
     * @param  Request  $request      当前请求
     * @param  Closure  $next         下一个中间件/处理器
     * @param  string   $maxAttempts  最大尝试次数（默认 60）
     * @param  string   $decayMinutes 衰减周期（分钟，默认 1）
     * @return Response
     */
    public function handle(Request $request, Closure $next, string $maxAttempts = '60', string $decayMinutes = '1'): Response
    {
        $key = $this->resolveRequestKey($request);

        if (RateLimiter::tooManyAttempts($key, (int) $maxAttempts)) {
            return $this->buildRateLimitedResponse($key, (int) $decayMinutes);
        }

        RateLimiter::hit($key, (int) $decayMinutes * 60);

        $response = $next($request);

        $this->addRateLimitHeaders($response, $key, (int) $maxAttempts);

        return $response;
    }

    /**
     * 解析限流键名
     *
     * 已认证用户使用 user_id + 请求指纹，
     * 未认证用户使用 IP + 请求指纹。
     */
    protected function resolveRequestKey(Request $request): string
    {
        $user = $request->user();

        if ($user) {
            return 'rate_limit:user:' . $user->getAuthIdentifier() . ':' . $request->fingerprint();
        }

        return 'rate_limit:ip:' . $request->ip() . ':' . $request->fingerprint();
    }

    /**
     * 构建限流响应（HTTP 429 Too Many Requests）
     */
    protected function buildRateLimitedResponse(string $key, int $decayMinutes): Response
    {
        $seconds = RateLimiter::availableIn($key);

        return ApiResponse::error(
            code: CoreErrorCode::RateLimited->value,
            message: CoreErrorCode::RateLimited->message(),
            httpStatus: CoreErrorCode::RateLimited->httpStatus(),
        )->withHeaders([
            'Retry-After' => $seconds,
            'X-RateLimit-Limit' => '-',
            'X-RateLimit-Remaining' => 0,
            'X-RateLimit-Reset' => now()->addSeconds($seconds)->getTimestamp(),
        ]);
    }

    /**
     * 添加限流状态响应头
     */
    protected function addRateLimitHeaders(Response $response, string $key, int $maxAttempts): void
    {
        $remaining = max(0, $maxAttempts - RateLimiter::attempts($key) + 1);
        $resetAt = now()->addSeconds(RateLimiter::availableIn($key))->getTimestamp();

        $response->headers->set('X-RateLimit-Limit', (string) $maxAttempts);
        $response->headers->set('X-RateLimit-Remaining', (string) $remaining);
        $response->headers->set('X-RateLimit-Reset', (string) $resetAt);
    }
}
