<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Messenger;

use Prometheus\CollectorRegistry;
use Prometheus\Counter;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Messenger\Stamp\HandledStamp;
use Throwable;

/**
 * Middleware that counts handled and failed messages for Prometheus.
 *
 * Increments messenger_messages_handled_total on success and
 * messenger_messages_failed_total on failure, labeled by message type.
 */
final class MessengerMetricsMiddleware implements MiddlewareInterface
{
    private const string NAMESPACE = 'classeo';

    private Counter $handledTotal;
    private Counter $failedTotal;

    public function __construct(CollectorRegistry $registry)
    {
        $this->handledTotal = $registry->getOrRegisterCounter(
            self::NAMESPACE,
            'messenger_messages_handled_total',
            'Total messages handled successfully',
            ['message'],
        );

        $this->failedTotal = $registry->getOrRegisterCounter(
            self::NAMESPACE,
            'messenger_messages_failed_total',
            'Total messages that failed',
            ['message'],
        );
    }

    public function handle(Envelope $envelope, StackInterface $stack): Envelope
    {
        $messageLabel = ClassNameHelper::short($envelope->getMessage()::class);

        try {
            $envelope = $stack->next()->handle($envelope, $stack);
        } catch (Throwable $e) {
            $this->safeIncrement($this->failedTotal, $messageLabel);

            throw $e;
        }

        /** @var HandledStamp[] $handledStamps */
        $handledStamps = $envelope->all(HandledStamp::class);
        if ($handledStamps !== []) {
            $this->safeIncrement($this->handledTotal, $messageLabel);
        }

        return $envelope;
    }

    private function safeIncrement(Counter $counter, string $label): void
    {
        try {
            $counter->inc([$label]);
        } catch (Throwable) {
            // Metrics storage failure must not break message handling
        }
    }
}
