<?php

namespace Comitium5\DesignerBundle\Command\RabbitMqConsumers;

use Comitium5\DesignerBundle\Entity\Page;
use Comitium5\DesignerBundle\Factory\Locale\LocaleFactory;
use Comitium5\DesignerBundle\Model\Interfaces\LocaleInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PagePublisherInterface;
use Comitium5\DesignerBundle\Model\Interfaces\Page\PageRepositoryInterface;
use Comitium5\DesignerBundle\UseCase\Page\ChangeStatePageUseCase;
use Comitium5\DesignerBundle\ValueObjects\PageMessageObject;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Message\AMQPMessage;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;

/**
 * Class PageDeletedConsumerCommand
 *
 * @author Oscar Jimenez <oscarjg19.developer@gmail.com>
 * @package Comitium5\DesignerBundle\Command\RabbitMqConsumers
 */
class PageStateChangedConsumerCommand extends AbstractRabbitMqConsumerCommand implements ContainerAwareInterface
{
    use ContainerAwareTrait;

    /**
     * @return string
     */
    protected function commandName(): string
    {
        return 'comitium:designer:consumer:page:state-changed';
    }

    /**
     * @return string
     */
    protected function commandDescription(): string
    {
        return 'RabbitMQ consumer which create a queue to consume pages after state changes';
    }

    /**
     * @param OutputInterface $output
     * @param int $maxMessages
     *
     * @return \Closure
     */
    protected function getClosure(OutputInterface $output, int $maxMessages): \Closure
    {
        return function (AMQPMessage $msg) use ($output, $maxMessages) {
            /**
             * @var AMQPChannel $channel
             */
            $channel = $msg->getChannel();
            $deliveryTag = $msg->getDeliveryTag();

            try {
                $output->writeln(
                    sprintf('[x] Received: %s', $msg->body),
                    OutputInterface::VERBOSITY_VERBOSE
                );

                $this->applyUseCase(new PageMessageObject($msg->getBody()));

                $channel->basic_ack($deliveryTag);

                $output->writeln(
                    sprintf('Done - ack with tag %s', $deliveryTag),
                    OutputInterface::VERBOSITY_VERBOSE
                );

                $this->handleLimitMessages($output, $maxMessages);
            } catch (\Exception $e) {
                $channel->basic_reject($deliveryTag, false);

                $output->writeln(
                    sprintf('An error has been produced: %s', $e->getMessage()),
                    OutputInterface::VERBOSITY_VERBOSE
                );
            }
        };
    }

    /**
     * @param PageMessageObject $pageMessageObject
     *
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    private function applyUseCase(PageMessageObject $pageMessageObject)
    {
        $useCase = new ChangeStatePageUseCase(
            $this->pageRepository(),
            $this->pagePublisher()
        );

        $useCase(
            $pageMessageObject->getId(),
            $pageMessageObject->isPublished() ?
                ChangeStatePageUseCase::PUBLISHED :
                ChangeStatePageUseCase::UNPUBLISHED,
            $this->getLocale($pageMessageObject)
        );
    }

    private function getLocale(PageMessageObject $pageMessageObject): ?LocaleInterface
    {
        $localeData = $pageMessageObject->getCustomData()['locale'] ?? null;
        return $localeData ? (new LocaleFactory())->create($localeData) : null;
    }

    /**
     * @return PageRepositoryInterface
     */
    private function pageRepository(): PageRepositoryInterface
    {
        return $this
            ->container
            ->get('doctrine')
            ->getManager()
            ->getRepository(Page::class);
    }

    /**
     * @return PagePublisherInterface
     */
    private function pagePublisher(): PagePublisherInterface
    {
        return $this
            ->container
            ->get('designer.publisher.page');
    }
}