<?php

/*
 * @author TRYDEA - 2024
 */

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

use Webapp\Core\Entity\AbstractVariable;
use Webapp\Core\Entity\Device;
use Webapp\Core\Entity\GeneratorVariable;
use Webapp\Core\Entity\SemiAutomaticVariable;
use Webapp\Core\Entity\SimpleVariable;
use Webapp\FileManagement\Dto\Webapp\VariableAndTestDto;
use Webapp\FileManagement\Dto\Webapp\VariableCollectionDto;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\AbstractRootNormalizer;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\XmlExportConfig;
use Webapp\FileManagement\Service\XmlUtils\Normalizer\XmlImportConfig;

/**
 * @template-extends AbstractRootNormalizer<VariableCollectionDto, VariableCollectionDtoXml>
 *
 * @psalm-type VariableCollectionDtoXml = array{
 *     "adonis.modeleMetier.projetDeSaisie.variables:Materiel": Device[],
 *     "adonis.modeleMetier.projetDeSaisie.variables:Variable": VariableAndTestDto[],
 *     "adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto": VariableAndTestDto[],
 *     "adonis.modeleMetier.projetDeSaisie.variables:VariableGeneratrice": VariableAndTestDto[]
 * }
 */
class VariableCollectionDtoNormalizer extends AbstractRootNormalizer
{
    protected const TAG_MATERIEL = 'adonis.modeleMetier.projetDeSaisie.variables:Materiel';
    protected const TAG_VARIABLE = 'adonis.modeleMetier.projetDeSaisie.variables:Variable';
    protected const TAG_VARIABLE_SEMI_AUTO = 'adonis.modeleMetier.projetDeSaisie.variables:VariableSemiAuto';
    protected const TAG_VARIABLE_GENERATRICE = 'adonis.modeleMetier.projetDeSaisie.variables:VariableGeneratrice';

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

    protected function getImportDataConfig(array $context): array
    {
        return [
            self::TAG_MATERIEL => XmlImportConfig::values(Device::class, true),
            self::TAG_VARIABLE => XmlImportConfig::values(VariableAndTestDto::class, true),
            self::TAG_VARIABLE_GENERATRICE => XmlImportConfig::values(VariableAndTestDto::class, true),
            self::TAG_VARIABLE_SEMI_AUTO => XmlImportConfig::values(VariableAndTestDto::class, true),
        ];
    }

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

    protected function completeImportedObject($object, $data, ?string $format, array $context): VariableCollectionDto
    {
        $variables = array_merge($data[self::TAG_VARIABLE], $data[self::TAG_VARIABLE_GENERATRICE], $data[self::TAG_VARIABLE_SEMI_AUTO]);

        $object->materiels = $data[self::TAG_MATERIEL];
        $object->variables = VariableAndTestDtoNormalizer::getVariablesAndCreateTests($variables, $this->serializer, $format, $context);

        return $object;
    }

    public function denormalize($data, string $type, ?string $format = null, array $context = [])
    {
        self::initDenormalizeContext($context);

        if (!isset($data[self::TAG_MATERIEL]) && !isset($data[self::TAG_VARIABLE])) {
            $object = new VariableCollectionDto();
            $object->variables[] = $this->serializer->denormalize($data, AbstractVariable::class, $format, $context);

            return $object;
        }

        return parent::denormalize($data, $type, $format, $context);
    }

    public function normalize($object, ?string $format = null, array $context = []): array
    {
        if (!isset($context[self::WRITER_HELPER]) || !isset($context[self::REFERENCE])) {
            self::initNormalizeContext(null, $context, 1); // Pass index 0 which correspond to AdonisVersion
        }

        $normalized = parent::normalize($object, $format, $context); // TODO: Change the autogenerated stub

        $writerHelper = self::getWriterHelper($context);
        foreach ($normalized[self::TAG_VARIABLE_SEMI_AUTO] as &$semiAuto) {
            if (isset($semiAuto[SemiAutomaticVariableNormalizer::ATTR_MATERIEL])) {
                $semiAuto[SemiAutomaticVariableNormalizer::ATTR_MATERIEL] = $writerHelper->get('/Device.'.$semiAuto[SemiAutomaticVariableNormalizer::ATTR_MATERIEL]);
            }
        }

        return $normalized;
    }

    protected function extractData($object, array $context): array
    {
        $simpleVariables = array_values(array_filter($object->variables, fn (AbstractVariable $variable) => $variable instanceof SimpleVariable));
        $semiAutoVariables = array_values(array_filter($object->variables, fn (AbstractVariable $variable) => $variable instanceof SemiAutomaticVariable));
        $generatriceVariables = array_values(array_filter($object->variables, fn (AbstractVariable $variable) => $variable instanceof GeneratorVariable));

        $data = [
            self::TAG_VARIABLE => new XmlExportConfig($simpleVariables, false),
            self::TAG_VARIABLE_SEMI_AUTO => new XmlExportConfig($semiAutoVariables, false),
            self::TAG_VARIABLE_GENERATRICE => new XmlExportConfig($generatriceVariables, false),
            self::TAG_MATERIEL => new XmlExportConfig($object->materiels, false),
        ];

        return array_merge(parent::extractData($object, $context), $data);
    }
}
