<?php

declare(strict_types=1);

namespace NoahVet\Reef\RabbitMQ\Consumer;

use NoahVet\Reef\RabbitMQ\InternalQueueTrait;
use NoahVet\Reef\RabbitMQ\Message\Handler\ReefAMQPMessageHandlerInterface;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Connection\AbstractConnection;
use PhpAmqpLib\Message\AMQPMessage;
use Psr\Log\LoggerInterface;

class ReefMQConsumer implements ReefMQConsumerInterface
{
    use InternalQueueTrait;

    private AMQPChannel $channel;

    public function __construct(
        AbstractConnection $connection,
        private readonly ReefAMQPMessageHandlerInterface $messageHandler,
        private readonly LoggerInterface $logger,
        private readonly string $queueName,
    ) {
        $this->channel = $connection->channel();
        $this->declareQueue($this->channel, $this->queueName);

        $this->channel->basic_qos(0, 1, null);
        $this->channel->basic_consume(
            $this->queueName,
            'reef-internal',
            false,
            false,
            false,
            false,
            [$this, 'onRawMessage'],
        );

        $this->channel->consume();
    }

    public function onRawMessage(AMQPMessage $message): void
    {
        try {
            $this->logger->debug(
                'Received AMQP Message',
                [
                    'properties' => $message->get_properties(),
                    'body' => $message->getBody(),
                ],
            );

            $contentEncoding = $message->get_properties()['content_encoding'] ?? $message->getContentEncoding();

            if ('application/json' !== $contentEncoding) {
                $this->logger->warning(
                    'Non JSON-encoded message is ignored',
                    [
                        'properties' => $message->get_properties(),
                        'body' => $message->getBody(),
                    ],
                );

                return;
            }

            $this->messageHandler->handleMessage($message);

            $this->logger->debug('Message processed successfully');

            $message->ack();
        } catch (\Exception $e) {
            $this->logger->error(
                $e->getMessage(),
                [
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ],
            );

            $message->nack();
        }
    }

    public function consume(float $timeout = 10.0): void
    {
        $this->channel->consume($timeout);
    }

    public function isOpen(): bool
    {
        return $this->channel->is_open();
    }

    public function stopConsume(): void
    {
        $this->channel->stopConsume();
        $this->channel->close();
    }
}
