<?php
declare(strict_types=1);

namespace App\Service;

use ApiPlatform\Api\IriConverterInterface;
use InvalidArgumentException;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * Sends private and encrypted mercure updates to the target users.
 *
 * Updates inform of modifications on entities : updates, creations, deletions.
 *
 * The update topic is the id of the recipient user.
 * The content is a json containing the iri of the entity, the operation type, and the current data of this entity
 */
class MercureHub
{
    public const UPDATE = 'update';
    public const CREATE = 'create';
    public const DELETE = 'delete';

    public function __construct(
        private HubInterface          $hub,
        private SerializerInterface   $serializer,
        private EncoderInterface      $encoder,
        private IriConverterInterface $iriConverter
    ) {}

    protected function createUpdate(int $accessId, string $data): Update
    {
        return new Update(
            "access/{$accessId}",
            $data,
            true
        );
    }

    /**
     * Send an update to the
     *
     * @param mixed $entity
     * @param int $accessId
     * @param string $operationType
     */
    public function publish(int $accessId, mixed $entity, string $operationType = self::UPDATE): void
    {
        if (!in_array($operationType, [self::UPDATE, self::CREATE, self::DELETE], true)) {
            throw new InvalidArgumentException('Invalid operation type');
        }

        $data = $this->encoder->encode([
            'iri' => $this->iriConverter->getIriFromResource($entity),
            'operation' => $operationType,
            'data' => $this->serializer->serialize($entity, 'jsonld')
        ], 'jsonld');

        $this->hub->publish(
            $this->createUpdate($accessId, $data)
        );
    }

    /**
     * @param mixed $entity
     * @param int $accessId
     */
    public function publishUpdate(int $accessId, mixed $entity): void {
        $this->publish($accessId, $entity, self::UPDATE);
    }

    /**
     * @param mixed $entity
     * @param int $accessId
     */
    public function publishCreate(int $accessId, mixed $entity): void {
        $this->publish($accessId, $entity, self::CREATE);
    }

    /**
     * @param mixed $entity
     * @param int $accessId
     */
    public function publishDelete(int $accessId, mixed $entity): void {
        $this->publish($accessId, $entity, self::DELETE);
    }
}
