<?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\ExportGenerationContext;
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\Export\Declarations;
use Chill\PersonBundle\Export\Helper\FilterListAccompanyingPeriodHelperInterface;
use Chill\PersonBundle\Export\Helper\ListAccompanyingPeriodHelper;
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\Form\FormBuilderInterface;

/**
 * List the persons having an accompanying period, with the accompanying period details.
 */
final readonly class ListPersonWithAccompanyingPeriodDetails implements ListInterface, GroupedExportInterface
{
    public function __construct(
        private ListPersonHelper $listPersonHelper,
        private ListAccompanyingPeriodHelper $listAccompanyingPeriodHelper,
        private EntityManagerInterface $entityManager,
        private RollingDateConverterInterface $rollingDateConverter,
        private FilterListAccompanyingPeriodHelperInterface $filterListAccompanyingPeriodHelper,
    ) {}

    public function buildForm(FormBuilderInterface $builder)
    {
        $builder->add('address_date', 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' => $formData['address_date']->normalize()];
    }

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

    public function getFormDefaultData(): array
    {
        return ['address_date' => 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 with details of period, according to various filters.';
    }

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

    public function getLabels($key, array $values, $data)
    {
        if (\in_array($key, $this->listPersonHelper->getAllKeys(), true)) {
            return $this->listPersonHelper->getLabels($key, $values, $data);
        }

        return $this->listAccompanyingPeriodHelper->getLabels($key, $values, $data);
    }

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

    public function getResult($query, $data, 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 with period details';
    }

    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, 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."'"));

        $this->filterListAccompanyingPeriodHelper->addFilterAccompanyingPeriods($qb, $requiredModifiers, $acl, $context->byUser, $data);

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

        AccompanyingCourseExportHelper::addClosingMotiveExclusionClause($qb);

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

        return $qb;
    }

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

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