<?php

namespace App\Middlewares;

use App\Core\Request;
use App\Core\Response;

/**
 * Rate Limit Middleware
 * Limits the number of requests per IP address using file-based counters.
 * Protects sensitive endpoints (login, register) from Brute Force attacks.
 */
class RateLimitMiddleware
{
    /**
     * Maximum allowed requests within the time window
     */
    private int $maxAttempts;

    /**
     * Time window in seconds
     */
    private int $decaySeconds;

    public function __construct(int $maxAttempts = 5, int $decaySeconds = 60)
    {
        $this->maxAttempts  = $maxAttempts;
        $this->decaySeconds = $decaySeconds;
    }

    public function handle(Request $request, Response $response): void
    {
        $ip  = $this->getClientIp();
        $key = 'rate_' . md5($ip . '_' . $request->getPath());

        $storageDir = APP_ROOT . '/storage/rate_limits';
        if (!is_dir($storageDir)) {
            mkdir($storageDir, 0750, true);
        }

        $filePath = $storageDir . '/' . $key . '.json';

        $data = ['count' => 0, 'expires_at' => time() + $this->decaySeconds];

        if (file_exists($filePath)) {
            $raw = json_decode(file_get_contents($filePath), true);
            if ($raw && isset($raw['expires_at']) && $raw['expires_at'] > time()) {
                // Window still active — use existing data
                $data = $raw;
            }
            // If window expired, fall through and reset (overwrite with fresh data below)
        }

        $data['count']++;

        if ($data['count'] > $this->maxAttempts) {
            $retryAfter = max(0, $data['expires_at'] - time());
            $response->setHeader('Retry-After', (string)$retryAfter);
            $response->json([
                'error'   => 'Too Many Requests',
                'message' => "You have exceeded the maximum number of {$this->maxAttempts} attempts. Please try again in {$retryAfter} seconds."
            ], 429);
            return;
        }

        // Persist the updated counter
        file_put_contents($filePath, json_encode($data), LOCK_EX);
    }

    /**
     * Get real client IP, accounting for proxies
     */
    private function getClientIp(): string
    {
        $headers = [
            'HTTP_CF_CONNECTING_IP',   // Cloudflare
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_REAL_IP',
            'REMOTE_ADDR'
        ];

        foreach ($headers as $header) {
            if (!empty($_SERVER[$header])) {
                // X-Forwarded-For can be a comma-separated list; take first
                $ip = trim(explode(',', $_SERVER[$header])[0]);
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return '0.0.0.0';
    }
}
