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

use Chill\ActivityBundle\Entity\Activity;
use Chill\ActivityBundle\Entity\ActivityType;
use Chill\ActivityBundle\Repository\ActivityTypeRepositoryInterface;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
use Chill\PersonBundle\Export\Declarations;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;

final readonly class ByActivityTypeAggregator implements AggregatorInterface
{
    private const PREFIX = 'acp_by_activity_type_agg';

    public function __construct(
        private RollingDateConverterInterface $rollingDateConverter,
        private ActivityTypeRepositoryInterface $activityTypeRepository,
        private TranslatableStringHelperInterface $translatableStringHelper,
    ) {}

    public function buildForm(FormBuilderInterface $builder): void
    {
        $builder
            ->add('after_date', PickRollingDateType::class, [
                'required' => false,
                'label' => 'export.aggregator.acp.by_activity_type.after_date',
            ])
            ->add('before_date', PickRollingDateType::class, [
                'required' => false,
                'label' => 'export.aggregator.acp.by_activity_type.before_date',
            ]);
    }

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

    public function normalizeFormData(array $formData): array
    {
        return ['after_date' => $formData['after_date']->normalize(), 'before_date' => $formData['before_date']->normalize()];
    }

    public function denormalizeFormData(array $formData, int $fromVersion): array
    {
        return ['after_date' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['after_date']), 'before_date' => \Chill\MainBundle\Service\RollingDate\RollingDate::fromNormalized($formData['before_date'])];
    }

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

    public function getLabels($key, array $values, mixed $data): callable
    {
        return function (int|string|null $value): string {
            if ('_header' === $value) {
                return 'export.aggregator.acp.by_activity_type.activity_type';
            }

            if ('' === $value || null === $value || null === $activityType = $this->activityTypeRepository->find($value)) {
                return '';
            }

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

    public function getQueryKeys($data): array
    {
        return [self::PREFIX.'_actype_id'];
    }

    public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
    {
        return 'export.aggregator.acp.by_activity_type.title';
    }

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

    public function alterQuery(QueryBuilder $qb, $data, \Chill\MainBundle\Export\ExportGenerationContext $exportGenerationContext): void
    {
        $p = self::PREFIX;

        // we make a left join, with acp having at least one activity of the given type
        $exists = 'EXISTS (SELECT 1 FROM '.Activity::class." {$p}_activity WHERE {$p}_activity.accompanyingPeriod = acp AND {$p}_activity.activityType = {$p}_activity_type";

        if (null !== $data['after_date']) {
            $exists .= " AND {$p}_activity.date > :{$p}_after_date";
            $qb->setParameter("{$p}_after_date", $this->rollingDateConverter->convert($data['after_date']));
        }

        if (null !== $data['before_date']) {
            $exists .= " AND {$p}_activity.date < :{$p}_before_date";
            $qb->setParameter("{$p}_before_date", $this->rollingDateConverter->convert($data['before_date']));
        }

        $exists .= ')';

        $qb->leftJoin(
            ActivityType::class,
            "{$p}_activity_type",
            Join::WITH,
            $exists
        );

        $qb
            ->addSelect("{$p}_activity_type.id AS {$p}_actype_id")
            ->addGroupBy("{$p}_actype_id");
    }

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