<?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\Aggregator;

use Chill\ActivityBundle\Export\Declarations;
use Chill\ActivityBundle\Repository\ActivityReasonCategoryRepository;
use Chill\ActivityBundle\Repository\ActivityReasonRepository;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportElementValidatedInterface;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class ActivityReasonAggregator implements AggregatorInterface, ExportElementValidatedInterface
{
    public function __construct(
        protected ActivityReasonCategoryRepository $activityReasonCategoryRepository,
        protected ActivityReasonRepository $activityReasonRepository,
        protected TranslatableStringHelper $translatableStringHelper,
    ) {}

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

    public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
    {
        // add select element
        if ('reasons' === $data['level']) {
            $elem = 'actreasons.id';
            $alias = 'activity_reasons_id';
        } elseif ('categories' === $data['level']) {
            $elem = 'actreasoncat.id';
            $alias = 'activity_categories_id';
        } else {
            throw new \RuntimeException('The data provided are not recognized.');
        }

        $qb->addSelect($elem.' as '.$alias);

        // make a jointure only if needed
        if (!\in_array('actreasons', $qb->getAllAliases(), true)) {
            $qb->leftJoin('activity.reasons', 'actreasons');
        }

        // join category if necessary
        if ('activity_categories_id' === $alias) {
            // add join only if needed
            if (!\in_array('actreasoncat', $qb->getAllAliases(), true)) {
                $qb->join('actreasons.category', 'actreasoncat');
            }
        }

        $qb->addGroupBy($alias);
    }

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

    public function buildForm(FormBuilderInterface $builder): void
    {
        $builder->add(
            'level',
            ChoiceType::class,
            [
                'choices' => [
                    'By reason' => 'reasons',
                    'By category of reason' => 'categories',
                ],
                'multiple' => false,
                'expanded' => true,
                'label' => "Reason's level",
            ]
        );
    }

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

    public function normalizeFormData(array $formData): array
    {
        return ['level' => $formData['level']];
    }

    public function denormalizeFormData(array $formData, int $fromVersion): array
    {
        return ['level' => $formData['level']];
    }

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

    public function getLabels($key, array $values, $data): callable
    {
        return function ($value) use ($data) {
            if ('_header' === $value) {
                return 'reasons' === $data['level'] ? 'Group by reasons' : 'Group by categories of reason';
            }

            if (null === $value || '' === $value) {
                return '';
            }

            switch ($data['level']) {
                case 'reasons':
                    $r = $this->activityReasonRepository->find($value);

                    return sprintf(
                        '%s > %s',
                        $this->translatableStringHelper->localize($r->getCategory()->getName()),
                        $this->translatableStringHelper->localize($r->getName())
                    );

                case 'categories':
                    $c = $this->activityReasonCategoryRepository->find($value);

                    return $this->translatableStringHelper->localize($c->getName());
            }
        };
    }

    public function getQueryKeys($data): array
    {
        // add select element
        if ('reasons' === $data['level']) {
            return ['activity_reasons_id'];
        }

        if ('categories' === $data['level']) {
            return ['activity_categories_id'];
        }

        throw new \RuntimeException('The data provided are not recognised.');
    }

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

    public function validateForm($data, ExecutionContextInterface $context): void
    {
        if (null === $data['level']) {
            $context
                ->buildViolation("The reasons's level should not be empty.")
                ->addViolation();
        }
    }
}
