<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Monitoring;

use Prometheus\CollectorRegistry;
use Prometheus\RenderTextFormat;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Routing\Attribute\Route;

/**
 * Exposes Prometheus metrics endpoint.
 *
 * Collects and exposes application metrics for Prometheus scraping.
 * Metrics include request latency, error rates, and custom business metrics.
 *
 * Security: In production, this endpoint is restricted to internal Docker network IPs.
 * For additional security, configure your reverse proxy (nginx/traefik) to block
 * external access to /metrics.
 *
 * @see Story 1.8 - T3.3: Expose /metrics endpoint in backend
 */
#[Route('/metrics', name: 'prometheus_metrics', methods: ['GET'])]
final readonly class MetricsController
{
    /**
     * Internal network CIDR ranges allowed to access metrics.
     */
    private const array ALLOWED_NETWORKS = [
        '10.0.0.0/8',
        '172.16.0.0/12',
        '192.168.0.0/16',
        '127.0.0.0/8',
    ];

    public function __construct(
        private CollectorRegistry $registry,
        private HealthMetricsCollectorInterface $healthMetrics,
        private MessengerMetricsCollectorInterface $messengerMetrics,
        private string $appEnv = 'dev',
    ) {
    }

    public function __invoke(Request $request): Response
    {
        // In production, restrict to internal networks only
        if ($this->appEnv === 'prod' && !$this->isInternalRequest($request)) {
            throw new AccessDeniedHttpException('Metrics endpoint is restricted to internal networks.');
        }

        // Collect fresh metrics before rendering
        $this->healthMetrics->collect();
        $this->messengerMetrics->collect();

        $renderer = new RenderTextFormat();
        $metrics = $renderer->render($this->registry->getMetricFamilySamples());

        return new Response(
            $metrics,
            Response::HTTP_OK,
            ['Content-Type' => RenderTextFormat::MIME_TYPE],
        );
    }

    private function isInternalRequest(Request $request): bool
    {
        $clientIp = $request->getClientIp();
        if ($clientIp === null) {
            return false;
        }

        foreach (self::ALLOWED_NETWORKS as $network) {
            if ($this->ipInRange($clientIp, $network)) {
                return true;
            }
        }

        return false;
    }

    private function ipInRange(string $ip, string $cidr): bool
    {
        [$subnet, $bits] = explode('/', $cidr);
        $ip = ip2long($ip);
        $subnet = ip2long($subnet);
        $mask = -1 << (32 - (int) $bits);
        $subnet &= $mask;

        return ($ip & $mask) === $subnet;
    }
}
