<?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\ActivityBundle\Export\Filter\PersonFilters;

use Chill\ActivityBundle\Entity\ActivityReason;
use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Export\ExportGenerationContext;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class ActivityReasonFilter implements ExportElementValidatedInterface, FilterInterface
{
    use \Chill\MainBundle\Export\ExportDataNormalizerTrait;

    public function __construct(protected TranslatableStringHelper $translatableStringHelper, protected ActivityReasonRepository $activityReasonRepository) {}

    public function addRole(): ?string
    {
        return null;
    }

    public function alterQuery(QueryBuilder $qb, $data, ExportGenerationContext $exportGenerationContext): void
    {
        $where = $qb->getDQLPart('where');
        $join = $qb->getDQLPart('join');
        $clause = $qb->expr()->in('actreasons', ':selected_activity_reasons');

        if (!\in_array('actreasons', $qb->getAllAliases(), true)) {
            $qb->join('activity.reasons', 'actreasons');
        }

        if ($where instanceof Expr\Andx) {
            $where->add($clause);
        } else {
            $where = $qb->expr()->andX($clause);
        }

        $qb->add('where', $where);
        $qb->setParameter('selected_activity_reasons', $data['reasons']);
    }

    public function applyOn(): string
    {
        return Declarations::ACTIVITY_PERSON;
    }

    public function buildForm(FormBuilderInterface $builder): void
    {
        $builder->add('reasons', EntityType::class, [
            'class' => ActivityReason::class,
            'choice_label' => fn (ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getName()),
            'group_by' => fn (ActivityReason $reason) => $this->translatableStringHelper->localize($reason->getCategory()->getName()),
            'attr' => ['class' => 'select2 '],
            'multiple' => true,
            'expanded' => false,
        ]);
    }

    public function getNormalizationVersion(): int
    {
        return 1;
    }

    public function normalizeFormData(array $formData): array
    {
        return ['reasons' => $this->normalizeDoctrineEntity($formData['reasons'])];
    }

    public function denormalizeFormData(array $formData, int $fromVersion): array
    {
        return ['reasons' => $this->denormalizeDoctrineEntity($formData['reasons'], $this->activityReasonRepository)];
    }

    public function getFormDefaultData(): array
    {
        return [
            'reasons' => [],
        ];
    }

    public function describeAction($data, ExportGenerationContext $context): string|\Symfony\Contracts\Translation\TranslatableInterface|array
    {
        // collect all the reasons'name used in this filter in one array
        $reasonsNames = array_map(
            fn (ActivityReason $r): string => '"'.$this->translatableStringHelper->localize($r->getName()).'"',
            $this->activityReasonRepository->findBy(['id' => $data['reasons'] instanceof Collection ? $data['reasons']->toArray() : $data['reasons']])
        );

        return [
            'Filtered by reasons: only %list%',
            [
                '%list%' => implode(', ', $reasonsNames),
            ],
        ];
    }

    public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
    {
        return 'Filter by reason';
    }

    public function validateForm($data, ExecutionContextInterface $context): void
    {
        if (null === $data['reasons'] || 0 === \count($data['reasons'])) {
            $context
                ->buildViolation('At least one reason must be chosen')
                ->addViolation();
        }
    }
}
