<?php

/*
 * @author TRYDEA - 2024
 */

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

use Doctrine\Common\Collections\Collection;
use Webapp\Core\Entity\AbstractVariable;
use Webapp\Core\Entity\Device;
use Webapp\Core\Entity\SemiAutomaticVariable;
use Webapp\Core\Entity\Test;
use Webapp\Core\Enumeration\VariableTypeEnum;
use Webapp\FileManagement\Entity\RequestFile;
use Webapp\FileManagement\Exception\ParsingException;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\XmlImportConfig;

/**
 * @template-extends AbstractVariableNormalizer<SemiAutomaticVariable, SemiAutomaticVariableXmlDto>
 *
 * @psalm-import-type VariableTypeEnumId from VariableTypeEnum
 *
 * @psalm-type SemiAutomaticVariableXmlDto = array{
 *      "@nom": string,
 *      "@nomCourt": string,
 *      "@dateCreation": \DateTime,
 *      "@dateDerniereModification": \DateTime,
 *      "@saisieObligatoire": ?bool,
 *      "@nbRepetitionSaisies": int,
 *      "@ordre": ?int,
 *      "@commentaire": ?string,
 *      "@unite": ?string,
 *      "@valeurDefaut": ?bool,
 *      "@typeVariable": ?string,
 *      "@uniteParcoursType": ?string,
 *      "tests": Collection<int, Test>,
 *      "@debut": ?int,
 *      "@fin": ?int,
 *      "@materiel": ?Device,
 * }
 */
class SemiAutomaticVariableNormalizer extends AbstractVariableNormalizer
{
    public const XSI_TYPE_VARIABLE_SEMI_AUTO = 'adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto';

    protected const ATTR_DEBUT = '@debut';
    protected const ATTR_FIN = '@fin';

    public const ATTR_MATERIEL = '@materiel';

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

    public function supportsDenormalization($data, string $type, ?string $format = null, array $context = []): bool
    {
        if (parent::supportsDenormalization($data, $type, $format, $context)) {
            return true;
        } elseif (AbstractVariable::class !== $type) {
            return false;
        }

        // Manage target $type AbstractVariable
        if (isset($data[GeneratorVariableNormalizer::ATTR_NOM_GENERE])) {
            // It is a generator variable.
            return false;
        }

        return ($data[self::ATTR_XSI_TYPE] ?? '') === self::XSI_TYPE_VARIABLE_SEMI_AUTO || isset($data[self::ATTR_MATERIEL]);
    }

    /**
     * @throws ParsingException
     */
    protected function validateImportData($data, array $context): void
    {
        parent::validateImportData($data, $context);

        $readerHelper = self::getReaderHelper($context);
        if (!isset($data[self::ATTR_MATERIEL]) || !$readerHelper->exists($data[self::ATTR_MATERIEL])) {
            throw new ParsingException('', RequestFile::ERROR_UNKNOWN_MATERIAL);
        }
    }

    protected function getImportDataConfig(array $context): array
    {
        return array_merge(parent::getCommonImportDataConfig($context), [
            self::ATTR_DEBUT => 'int',
            self::ATTR_FIN => 'int',
            self::ATTR_MATERIEL => XmlImportConfig::reference(Device::class),
        ]);
    }

    protected function generateImportedObject($data, array $context): SemiAutomaticVariable
    {
        return new SemiAutomaticVariable('', '', 0, '', false, '', 0, 0);
    }

    protected function completeImportedObject($object, $data, ?string $format, array $context): SemiAutomaticVariable
    {
        $object = parent::completeImportedObject($object, $data, $format, $context);
        $object
            ->setType(array_search($data[self::ATTR_TYPE_VARIABLE] ?? self::TYPE_ALPHANUMERIQUE, self::TYPE_MAP, true))
            ->setStart($data[self::ATTR_DEBUT] ?? 0)
            ->setEnd($data[self::ATTR_FIN] ?? 0);

        /** @var ?Device $device */
        $device = $data[self::ATTR_MATERIEL];
        if (null !== $device) {
            $device->addManagedVariable($object);
        }

        return $object;
    }

    protected function extractData($object, array $context): array
    {
        return array_merge(
            [
                self::ATTR_XSI_TYPE => 'adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto',
            ],
            parent::extractCommonData($object, $context),
            [
                self::ATTR_DEBUT => $object->getStart(),
                self::ATTR_FIN => $object->getEnd(),
                self::ATTR_MATERIEL => null !== $object->getDevice() ? $object->getDevice()->getId() : null,
            ]
        );
    }
}
