<?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\PersonBundle\Export\Export;

use Chill\MainBundle\Export\AccompanyingCourseExportHelper;
use Chill\MainBundle\Export\FormatterInterface;
use Chill\MainBundle\Export\GroupedExportInterface;
use Chill\MainBundle\Export\ListInterface;
use Chill\MainBundle\Form\Type\PickRollingDateType;
use Chill\MainBundle\Service\RollingDate\RollingDate;
use Chill\MainBundle\Service\RollingDate\RollingDateConverterInterface;
use Chill\PersonBundle\Entity\AccompanyingPeriod;
use Chill\PersonBundle\Entity\Person;
use Chill\PersonBundle\Entity\Person\PersonCenterHistory;
use Chill\PersonBundle\Export\Declarations;
use Chill\PersonBundle\Export\Helper\ListPersonHelper;
use Chill\PersonBundle\Security\Authorization\PersonVoter;
use DateTimeImmutable;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\FormBuilderInterface;

/**
 * List the persons, having an accompanying period.
 *
 * Details of the accompanying period are not included
 */
final readonly class ListPersonHavingAccompanyingPeriod implements ListInterface, GroupedExportInterface
{
    private bool $filterStatsByCenters;

    public function __construct(
        private ListPersonHelper $listPersonHelper,
        private EntityManagerInterface $entityManager,
        private RollingDateConverterInterface $rollingDateConverter,
        ParameterBagInterface $parameterBag,
    ) {
        $this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center'];
    }

    public function buildForm(FormBuilderInterface $builder)
    {
        $builder->add('address_date_rolling', PickRollingDateType::class, [
            'label' => 'Data valid at this date',
            'help' => 'Data regarding center, addresses, and so on will be computed at this date',
        ]);
    }

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

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

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

    public function getFormDefaultData(): array
    {
        return ['address_date_rolling' => new RollingDate(RollingDate::T_TODAY)];
    }

    public function getAllowedFormattersTypes()
    {
        return [FormatterInterface::TYPE_LIST];
    }

    public function getDescription()
    {
        return 'export.list.person_with_acp.Create a list of people having an accompaying periods, according to various filters.';
    }

    public function getGroup(): string
    {
        return 'Exports of persons';
    }

    public function getLabels($key, array $values, $data)
    {
        return $this->listPersonHelper->getLabels($key, $values, $data);
    }

    public function getQueryKeys($data): array
    {
        return $this->listPersonHelper->getAllKeys();
    }

    public function getResult($query, $data, \Chill\MainBundle\Export\ExportGenerationContext $context): array
    {
        return $query->getQuery()->getResult(AbstractQuery::HYDRATE_SCALAR);
    }

    public function getTitle(): string|\Symfony\Contracts\Translation\TranslatableInterface
    {
        return 'export.list.person_with_acp.List peoples having an accompanying period';
    }

    public function getType(): string
    {
        return Declarations::PERSON_TYPE;
    }

    /**
     * param array{fields: string[], address_date: DateTimeImmutable} $data.
     */
    public function initiateQuery(array $requiredModifiers, array $acl, array $data, \Chill\MainBundle\Export\ExportGenerationContext $context): \Doctrine\ORM\QueryBuilder
    {
        $centers = array_map(static fn ($el) => $el['center'], $acl);

        $qb = $this->entityManager->createQueryBuilder();

        $qb->from(Person::class, 'person')
            ->join('person.accompanyingPeriodParticipations', 'acppart')
            ->join('acppart.accompanyingPeriod', 'acp')
            ->andWhere($qb->expr()->neq('acp.step', "'".AccompanyingPeriod::STEP_DRAFT."'"));

        if ($this->filterStatsByCenters) {
            $qb
                ->andWhere(
                    $qb->expr()->exists(
                        'SELECT 1 FROM '.PersonCenterHistory::class.' pch WHERE pch.person = person.id AND pch.center IN (:authorized_centers)'
                    )
                )->setParameter('authorized_centers', $centers);
        }

        $this->listPersonHelper->addSelect($qb, $this->rollingDateConverter->convert($data['address_date_rolling']));

        AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);

        $qb
            ->addOrderBy('person.lastName')
            ->addOrderBy('person.firstName')
            ->addOrderBy('person.id');

        return $qb;
    }

    public function requiredRole(): string
    {
        return PersonVoter::LISTS;
    }

    public function supportsModifiers()
    {
        return [Declarations::PERSON_TYPE, Declarations::ACP_TYPE];
    }
}
