<?php

declare(strict_types=1);

namespace App\Framework\Http\Middlewares;

use App\Framework\Config\Environment;
use App\Framework\Config\EnvKey;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\IpAddress;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewarePriority;
use App\Framework\Http\MiddlewarePriorityAttribute;
use App\Framework\Http\Next;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Responses\JsonResponse;
use App\Framework\Http\Status;

/**
 * Middleware to block sensitive routes in production environment
 */
#[MiddlewarePriorityAttribute(MiddlewarePriority::VERY_EARLY)]
final readonly class ProductionSecurityMiddleware implements HttpMiddleware
{
    /**
     * Routes that should be blocked in production
     */
    private const BLOCKED_ROUTES = [
        '/admin/discovery',
        '/admin/routes',
        '/admin/performance',
        '/admin/environment',
        '/debug',
        '/performance',
        '/api/debug',
    ];

    /**
     * Routes that require IP whitelist in production
     */
    private const IP_RESTRICTED_ROUTES = [
        '/admin',
        '/analytics',
        '/health',
        '/metrics',
    ];

    /**
     * Allowed IPs for admin access in production
     */
    private const ALLOWED_IPS = [
        '127.0.0.1',
        '::1',
        // Add your office/home IP here for production admin access
    ];

    public function __construct(
        private Environment $environment
    ) {
    }

    public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
    {
        // Only apply restrictions in production
        if ($this->environment->get(EnvKey::APP_ENV, 'production') !== 'production') {
            return $next($context);
        }

        $path = $context->request->path ?? '/';
        $clientIp = $context->request->server->getClientIp();

        // Block sensitive debug routes completely in production
        if ($this->isBlockedRoute($path)) {
            return $context->withResponse(
                new JsonResponse(
                    body: ['error' => 'Not Found'],
                    status: Status::NOT_FOUND
                )
            );
        }

        // Check IP whitelist for admin routes
        if ($this->isIpRestrictedRoute($path) && ! $this->isAllowedIp($clientIp)) {
            return $context->withResponse(
                new JsonResponse(
                    body: ['error' => 'Access Denied'],
                    status: Status::FORBIDDEN
                )
            );
        }

        return $next($context);
    }

    private function isBlockedRoute(string $path): bool
    {
        foreach (self::BLOCKED_ROUTES as $blockedRoute) {
            if (str_starts_with($path, $blockedRoute)) {
                return true;
            }
        }

        return false;
    }

    private function isIpRestrictedRoute(string $path): bool
    {
        foreach (self::IP_RESTRICTED_ROUTES as $restrictedRoute) {
            if (str_starts_with($path, $restrictedRoute)) {
                return true;
            }
        }

        return false;
    }

    private function isAllowedIp(?IpAddress $clientIp): bool
    {
        if ($clientIp === null) {
            return false;
        }

        $ipString = (string) $clientIp;

        // Check if IP is in whitelist
        if (in_array($ipString, self::ALLOWED_IPS, true)) {
            return true;
        }

        // Check if IP is from allowed environment variable
        $allowedIpsEnv = $this->environment->get('ADMIN_ALLOWED_IPS', '');
        if (! empty($allowedIpsEnv)) {
            $allowedIps = array_map('trim', explode(',', $allowedIpsEnv));

            return in_array($ipString, $allowedIps, true);
        }

        return false;
    }
}
