<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Export;

use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Entity\Regroupment;
use Chill\MainBundle\Form\Type\Export\AggregatorType;
use Chill\MainBundle\Form\Type\Export\ExportType;
use Chill\MainBundle\Form\Type\Export\FilterType;
use Chill\MainBundle\Repository\CenterRepositoryInterface;
use Chill\MainBundle\Repository\RegroupmentRepositoryInterface;

/**
 * @phpstan-type NormalizedData array{centers: array{centers: list<int>, regroupments: list<int>}, export: array{form: array<string, mixed>, version: int}, filters: array<string, array{enabled: boolean, form: array<string, mixed>, version: int}>, aggregators: array<string, array{enabled: boolean, form: array<string, mixed>, version: int}>, pick_formatter?: string, formatter: array{form: array<string, mixed>, version: int}}
 */
class ExportConfigNormalizer
{
    public function __construct(
        private readonly ExportManager $exportManager,
        private readonly CenterRepositoryInterface $centerRepository,
        private readonly RegroupmentRepositoryInterface $regroupmentRepository,
    ) {}

    /**
     * @return NormalizedData
     */
    public function normalizeConfig(string $exportAlias, array $formData): array
    {
        $exportData = $formData[ExportType::EXPORT_KEY];
        $export = $this->exportManager->getExport($exportAlias);

        $serialized = [
            'export' => [
                'form' => $export->normalizeFormData($exportData),
                'version' => $export->getNormalizationVersion(),
            ],
        ];

        $serialized['centers'] = [
            'centers' => array_values(array_map(static fn (Center $center) => $center->getId(), $formData['centers']['centers'] ?? [])),
            'regroupments' => array_values(array_map(static fn (Regroupment $group) => $group->getId(), $formData['centers']['regroupments'] ?? [])),
        ];

        $filtersSerialized = [];
        foreach ($formData[ExportType::FILTER_KEY] as $alias => $filterData) {
            $filter = $this->exportManager->getFilter($alias);
            $filtersSerialized[$alias][FilterType::ENABLED_FIELD] = (bool) $filterData[FilterType::ENABLED_FIELD];
            if ($filterData[FilterType::ENABLED_FIELD]) {
                $filtersSerialized[$alias]['form'] = $filter->normalizeFormData($filterData['form']);
                $filtersSerialized[$alias]['version'] = $filter->getNormalizationVersion();
            }
        }
        $serialized['filters'] = $filtersSerialized;

        $aggregatorsSerialized = [];
        foreach ($formData[ExportType::AGGREGATOR_KEY] as $alias => $aggregatorData) {
            $aggregator = $this->exportManager->getAggregator($alias);
            $aggregatorsSerialized[$alias][FilterType::ENABLED_FIELD] = (bool) $aggregatorData[AggregatorType::ENABLED_FIELD];
            if ($aggregatorData[AggregatorType::ENABLED_FIELD]) {
                $aggregatorsSerialized[$alias]['form'] = $aggregator->normalizeFormData($aggregatorData['form']);
                $aggregatorsSerialized[$alias]['version'] = $aggregator->getNormalizationVersion();
            }
        }
        $serialized['aggregators'] = $aggregatorsSerialized;

        if ($export instanceof ExportInterface) {
            $serialized['pick_formatter'] = $formData['pick_formatter'];
            $formatter = $this->exportManager->getFormatter($formData['pick_formatter']);
            $serialized['formatter']['form'] = $formatter->normalizeFormData($formData['formatter']);
            $serialized['formatter']['version'] = $formatter->getNormalizationVersion();
        } elseif ($export instanceof DirectExportInterface) {
            $serialized['formatter'] = ['form' => [], 'version' => 0];
        }

        return $serialized;
    }

    /**
     * @param NormalizedData $serializedData
     * @param bool           $replaceDisabledByDefaultData if true, when a filter is not enabled, the formDefaultData is set
     */
    public function denormalizeConfig(string $exportAlias, array $serializedData, bool $replaceDisabledByDefaultData = false): array
    {
        $export = $this->exportManager->getExport($exportAlias);

        if ($export instanceof ExportInterface) {
            $formatter = $this->exportManager->getFormatter($serializedData['pick_formatter']);
        } else {
            $formatter = null;
        }

        $filtersConfig = [];
        foreach ($serializedData['filters'] as $alias => $filterData) {
            $aggregator = $this->exportManager->getFilter($alias);
            $filtersConfig[$alias]['enabled'] = $filterData['enabled'];

            if ($filterData['enabled']) {
                $filtersConfig[$alias]['form'] = $aggregator->denormalizeFormData($filterData['form'], $filterData['version']);
            } elseif ($replaceDisabledByDefaultData) {
                $filtersConfig[$alias]['form'] = $aggregator->getFormDefaultData();
            }
        }

        $aggregatorsConfig = [];
        foreach ($serializedData['aggregators'] as $alias => $aggregatorData) {
            $aggregator = $this->exportManager->getAggregator($alias);
            $aggregatorsConfig[$alias]['enabled'] = $aggregatorData['enabled'];

            if ($aggregatorData['enabled']) {
                $aggregatorsConfig[$alias]['form'] = $aggregator->denormalizeFormData($aggregatorData['form'], $aggregatorData['version']);
            } elseif ($replaceDisabledByDefaultData) {
                $aggregatorsConfig[$alias]['form'] = $aggregator->getFormDefaultData();
            }
        }

        return [
            'export' => $export->denormalizeFormData($serializedData['export']['form'], $serializedData['export']['version']),
            'filters' => $filtersConfig,
            'aggregators' => $aggregatorsConfig,
            'pick_formatter' => $serializedData['pick_formatter'] ?? '',
            'formatter' => $formatter?->denormalizeFormData($serializedData['formatter']['form'], $serializedData['formatter']['version']),
            'centers' => [
                'centers' => array_values(array_filter(array_map(fn (int $id) => $this->centerRepository->find($id), $serializedData['centers']['centers']), fn ($item) => null !== $item)),
                'regroupments' => array_values(array_filter(array_map(fn (int $id) => $this->regroupmentRepository->find($id), $serializedData['centers']['regroupments']), fn ($item) => null !== $item)),
            ],
        ];
    }
}
