<?php

/*
 * @author TRYDEA - 2024
 */

namespace Webapp\FileManagement\Service\XmlUtils\Normalizer\Webapp;

use Webapp\Core\Entity\Device;
use Webapp\FileManagement\Dto\Common\ReferencesDto;
use Webapp\FileManagement\Dto\Webapp\DeviceDriverDto;
use Webapp\FileManagement\Entity\RequestFile;
use Webapp\FileManagement\Exception\ParsingException;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\AbstractXmlNormalizer;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\XmlImportConfig;

/**
 * @template-extends AbstractXmlNormalizer<Device, DeviceXmlDto>
 *
 * @psalm-type DeviceXmlDto = array{
 *      "@appareil": ?string,
 *      "@fabriquant": ?string,
 *      "@nom": ?string,
 *      "@type": ?string,
 *      driver: ?DeviceDriverDto
 * }
 */
class DeviceNormalizer extends AbstractXmlNormalizer
{
    protected const ATTR_NOM = '@nom';
    protected const ATTR_FABRIQUANT = '@fabriquant';
    protected const ATTR_APPAREIL = '@appareil';
    protected const ATTR_TYPE = '@type';
    protected const ATTR_VARIABLES = '@variables';
    protected const TAG_DRIVER = 'driver';

    protected function getClass(): string
    {
        return Device::class;
    }

    protected function getImportDataConfig(array $context): array
    {
        return [
            self::ATTR_NOM => 'string',
            self::ATTR_TYPE => 'string',
            self::ATTR_FABRIQUANT => 'string',
            self::ATTR_APPAREIL => 'string',
            self::TAG_DRIVER => XmlImportConfig::value(DeviceDriverDto::class),
        ];
    }

    protected function validateImportData($data, array $context): void
    {
        if (!isset($data[self::ATTR_NOM])) {
            throw new ParsingException('', RequestFile::ERROR_UNKNOWN_MATERIAL);
        }
    }

    protected function generateImportedObject($data, array $context): Device
    {
        return new Device();
    }

    /**
     * @psalm-param Device $object
     * @psalm-param mixed $data
     *
     * @psalm-assert array{
     *     "@nom": string|null,
     *     "@type": string|null,
     *     "@fabriquant": string|null,
     *     "@appareil": string|null,
     *     "driver": DeviceDriverDto|null
     * } $data
     */
    protected function completeImportedObject($object, $data, ?string $format, array $context): Device
    {
        $object
            ->setName($data[self::ATTR_NOM] ?? '')
            ->setType($data[self::ATTR_TYPE] ?? '')
            ->setManufacturer($data[self::ATTR_FABRIQUANT] ?? '')
            ->setAlias($data[self::ATTR_APPAREIL] ?? '');

        /** @var DeviceDriverDto|null $driver */
        $driver = $data[self::TAG_DRIVER];
        if (null !== $driver) {
            $driver->toDevice($object);
        }

        return $object;
    }

    protected function extractData($object, array $context): array
    {
        return [
            self::ATTR_NOM => $object->getName(),
            self::ATTR_FABRIQUANT => $object->getManufacturer(),
            self::ATTR_APPAREIL => $object->getAlias(),
            self::ATTR_TYPE => $object->getType(),
            self::ATTR_VARIABLES => new ReferencesDto($object->getManagedVariables()),
            self::TAG_DRIVER => DeviceDriverDto::fromDevice($object),
        ];
    }
}
