<?php

namespace App\Http\Middleware;

use App\Services\Security\SecurityEventLogger;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

class NgbvdRateLimiter
{
    private $logger;

    public function __construct(SecurityEventLogger $logger)
    {
        $this->logger = $logger;
    }

    public function handle(Request $request, Closure $next)
    {
        if ($request->is('api/*')) {
            return $next($request);
        }

        [$key, $maxAttempts, $decaySeconds, $category] = $this->limitFor($request);

        if (RateLimiter::tooManyAttempts($key, $maxAttempts)) {
            $retryAfter = RateLimiter::availableIn($key);

            $this->logger->log('rate_limit_exceeded', 'warning', [
                'category' => $category,
                'retry_after_seconds' => $retryAfter,
            ], $request);

            if ($request->expectsJson() || $request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Too many requests. Please wait and try again.',
                    'retry_after_seconds' => $retryAfter,
                ], 429)->header('Retry-After', $retryAfter);
            }

            return response()->view('errors.index', [
                'statusCode' => 429,
                'title' => 'Too Many Requests',
                'message' => 'Too many requests were received. Please wait and try again.',
                'reference' => 'rate-limit',
                'requestedResource' => $request->path(),
            ], 429)->header('Retry-After', $retryAfter);
        }

        RateLimiter::hit($key, $decaySeconds);

        return $next($request);
    }

    private function limitFor(Request $request): array
    {
        $actor = optional($request->user())->id ?: $request->ip();
        $path = trim($request->path(), '/');

        if ($request->is('login') || $request->is('password/*') || $request->is('register')) {
            return ['auth:'.$request->ip(), 10, 60, 'auth'];
        }

        if ($request->is('dataentry/import*') || $request->is('resources/postresource')) {
            return ['upload:'.$actor, 10, 60, 'file_upload'];
        }

        if ($request->is('download/*') || $request->is('exportToExcel') || $request->is('analysis/*')) {
            return ['expensive:'.$actor.':'.$path, 10, 3600, 'expensive_export_or_analysis'];
        }

        if (! $request->isMethodSafe()) {
            return ['write:'.$actor.':'.$path, 30, 60, 'write'];
        }

        return ['read:'.$actor, 120, 60, 'read'];
    }
}
