<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Response;
use Illuminate\Database\Capsule\Manager as DB;

class RateLimitMiddleware
{
    // endpoint => [max_hits, window_seconds]
    private const LIMITS = [
        '/api/auth/login'          => [5,  900],
        '/api/auth/register'       => [3,  3600],
        '/api/auth/forgot-password'=> [3,  3600],
        '/api/chrono/start'        => [10, 60],
        '/api/lots'                => [20, 3600],
    ];

    private const ADMIN_LIMIT = [30, 60];

    public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $path = $request->getUri()->getPath();
        $ip   = $this->getClientIp($request);

        [$max, $window] = $this->getLimit($path);

        if ($max > 0) {
            $count = $this->getHitCount($ip, $path, $window);

            if ($count >= $max) {
                $response = new Response();
                $response->getBody()->write(json_encode([
                    'error'   => true,
                    'code'    => 'RATE_LIMIT',
                    'message' => 'Trop de requêtes — réessayez plus tard'
                ]));
                return $response
                    ->withHeader('Content-Type', 'application/json')
                    ->withHeader('Retry-After', (string)$window)
                    ->withStatus(429);
            }

            $this->recordHit($ip, $path);
        }

        return $handler->handle($request);
    }

    private function getLimit(string $path): array
    {
        foreach (self::LIMITS as $pattern => $limit) {
            if (str_starts_with($path, $pattern)) {
                return $limit;
            }
        }
        if (str_starts_with($path, '/api/admin/')) {
            return self::ADMIN_LIMIT;
        }
        return [0, 0]; // Pas de limite
    }

    private function getHitCount(string $ip, string $path, int $window): int
    {
        try {
            $result = DB::table('rate_limits')
                ->where('ip', $ip)
                ->where('endpoint', $path)
                ->where('created_at', '>', date('Y-m-d H:i:s', time() - $window))
                ->count();
            return (int)$result;
        } catch (\Exception $e) {
            return 0; // En cas d'erreur DB, laisser passer
        }
    }

    private function recordHit(string $ip, string $path): void
    {
        try {
            DB::table('rate_limits')->insert([
                'ip'         => $ip,
                'endpoint'   => $path,
                'created_at' => date('Y-m-d H:i:s')
            ]);
            // Purge anciens enregistrements (1h)
            DB::table('rate_limits')
                ->where('created_at', '<', date('Y-m-d H:i:s', time() - 3600))
                ->delete();
        } catch (\Exception $e) {
            // Silencieux
        }
    }

    private function getClientIp(ServerRequestInterface $request): string
    {
        $serverParams = $request->getServerParams();
        foreach (['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'] as $key) {
            if (!empty($serverParams[$key])) {
                return explode(',', $serverParams[$key])[0];
            }
        }
        return '0.0.0.0';
    }
}
