<?php

namespace Comitium5\DesignerBundle\Service\BlockContent;

use Comitium5\ApiClientBundle\Client\Services\BlockContentApiService;
use Comitium5\ApiClientBundle\Normalizer\RequestParametersNormalizer\GuzzleFormParametersNormalizer;
use Comitium5\ApiClientBundle\ValueObject\IdentifiedValue;
use Comitium5\ApiClientBundle\ValueObject\ParametersValue;
use Comitium5\CommonWidgetsBundle\Cache\MemoryCacheInterface;
use Comitium5\DesignerBundle\Helper\Utils;

/**
 * Class BlockContentService
 *
 * @author Carles Gómez <carles@bab-soft.com>
 * @package Comitium5\DesignerBundle\Service\BlockContent
 */
class BlockContentService
{
    private const BLOCK_CONTENT_KEY = 'designer_block_content_%s_%s';
    private const BLOCK_CONTENT_TTL = 30;

    /**
     * @var BlockContentApiService
     */
    private $blockContentApiService;

    /**
     * @var MemoryCacheInterface
     */
    private $cache;

    /**
     * BlockContentService constructor.
     *
     * @param BlockContentApiService $blockContentApiService
     * @param MemoryCacheInterface $cache
     */
    public function __construct(BlockContentApiService $blockContentApiService, MemoryCacheInterface $cache)
    {
        $this->blockContentApiService = $blockContentApiService;
        $this->cache                  = $cache;
    }

    /**
     * @param int $userId
     * @param int $entityId
     * @param string $resourceType
     *
     * @return array
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function block(int $userId, int $entityId, string $resourceType): array
    {
        if ($this->isBlocked($entityId, $resourceType)) {
            return [];
        }

        $response = $this
            ->blockContentApiService
            ->post(new ParametersValue([
                'user'        => $userId,
                'entityClass' => $resourceType,
                'entityId'    => $entityId,
            ]));

        if (!empty($response)) {
            $this->setRedisContent($entityId, $response, $resourceType);

            return $response;
        }

        return [];
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     *
     * @throws \GuzzleHttp\Exception\GuzzleException|\Exception
     */
    public function unblock(int $entityId, string $resourceType)
    {
        $blockContent = $this->getRedisContent($entityId, $resourceType);

        if (empty($blockContent) === false) {
            $this->removeRedisContent($entityId, $resourceType);

            $this
                ->blockContentApiService
                ->delete(new IdentifiedValue($blockContent['id']));
        }
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     * @param array $parameters
     *
     * @return array
     * @throws \Exception
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function update(int $entityId, string $resourceType, array $parameters): array
    {
        $blockedContent = $this->getRedisContent($entityId, $resourceType);

        $blockContent = $this
            ->blockContentApiService
            ->put(
                new IdentifiedValue($blockedContent['id']),
                new GuzzleFormParametersNormalizer($parameters)
            );

        if (false === empty($blockContent)) {
            $this->setRedisContent($entityId, $blockContent, $resourceType);
        }

        return $blockContent;
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     */
    public function refresh(int $entityId, string $resourceType)
    {
        $content = $this->getRedisContent($entityId, $resourceType);

        if (false === empty($content)) {
            $this->setRedisContent(
                $entityId,
                $content,
                $resourceType
            );
        }
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     *
     * @return bool
     */
    public function isBlocked(int $entityId, string $resourceType): bool
    {
        return false === empty($this->getRedisContent($entityId, $resourceType));
    }

    /**
     * @param int $entityId
     * @param array $blockContent
     * @param string $resourceType
     *
     * @return bool
     */
    private function setRedisContent(int $entityId, array $blockContent, string $resourceType): bool
    {
        return $this->cache->set(
            sprintf(self::BLOCK_CONTENT_KEY, $resourceType, $entityId),
            Utils::toJson($blockContent),
            self::BLOCK_CONTENT_TTL
        );
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     *
     * @return bool
     */
    private function removeRedisContent(int $entityId, string $resourceType): bool
    {
        return $this->cache->delete(sprintf(self::BLOCK_CONTENT_KEY, $resourceType, $entityId));
    }

    /**
     * @param int $entityId
     * @param string $resourceType
     *
     * @return array
     */
    public function getRedisContent(int $entityId, string $resourceType): array
    {
        $blockContent = $this->cache->get(sprintf(self::BLOCK_CONTENT_KEY, $resourceType, $entityId));

        if (false !== $blockContent) {
            return Utils::fromJson($blockContent);
        }

        return [];
    }
}
