<?php

declare(strict_types=1);

namespace NoahVet\Reef\RabbitMQ\Serializer\Normalizer;

use NoahVet\Reef\RabbitMQ\Task\ConsoleCommandTask;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class ConsoleCommandTaskNormalizer implements DenormalizerInterface, NormalizerInterface
{
    /**
     * @param iterable<Command> $commands
     */
    public function __construct(
        private readonly iterable $commands,
    ) {
    }

    /**
     * @param array{command: mixed, arguments: array<string, string|int>|null, spawn_new_process?: mixed} $data
     */
    public function denormalize($data, string $type, ?string $format = null, array $context = []): ConsoleCommandTask
    {
        $commandName = (string) ($data['command'] ?? '');
        $command = $this->getCommandByName($commandName);

        if (!$command) {
            throw new \Exception('Command not found : '.$commandName);
        }

        return new ConsoleCommandTask(
            $command,
            $data['arguments'] ?? [],
        );
    }

    /**
     * @return array<class-string|'*'|'object'|string, bool|null>
     */
    public function getSupportedTypes(?string $format): array
    {
        return [
            ConsoleCommandTask::class => true,
        ];
    }

    /**
     * @return array{type: string, command: string|null, arguments: array<string, string|int>}
     */
    public function normalize($object, ?string $format = null, array $context = []): array
    {
        \assert($object instanceof ConsoleCommandTask);

        return [
            'type' => ConsoleCommandTask::class,
            'command' => $object->command->getName(),
            'arguments' => $object->arguments,
        ];
    }

    /**
     * @param array<mixed> $context
     */
    public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool
    {
        return \is_array($data)
            && ($data['type'] ?? '') === ConsoleCommandTask::class
            && isset($data['command'])
            && \is_array($data['arguments'] ?? []);
    }

    /**
     * @param array<mixed> $context
     */
    public function supportsNormalization($data, ?string $format = null, array $context = []): bool
    {
        return $data instanceof ConsoleCommandTask;
    }

    protected function getCommandByName(string $name): ?Command
    {
        foreach ($this->commands as $command) {
            if ($command->getName() === $name) {
                return $command;
            }
        }

        return null;
    }
}
